@uns-kit/assistant-workflow 2.0.72
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/LICENSE +21 -0
- package/README.md +37 -0
- package/dist/clarification-policy.d.ts +64 -0
- package/dist/clarification-policy.d.ts.map +1 -0
- package/dist/clarification-policy.js +174 -0
- package/dist/clarification-policy.js.map +1 -0
- package/dist/decision.d.ts +37 -0
- package/dist/decision.d.ts.map +1 -0
- package/dist/decision.js +100 -0
- package/dist/decision.js.map +1 -0
- package/dist/definition-catalog-replay.d.ts +25 -0
- package/dist/definition-catalog-replay.d.ts.map +1 -0
- package/dist/definition-catalog-replay.js +54 -0
- package/dist/definition-catalog-replay.js.map +1 -0
- package/dist/definition-catalog.d.ts +45 -0
- package/dist/definition-catalog.d.ts.map +1 -0
- package/dist/definition-catalog.js +97 -0
- package/dist/definition-catalog.js.map +1 -0
- package/dist/definition-diagnostics.d.ts +17 -0
- package/dist/definition-diagnostics.d.ts.map +1 -0
- package/dist/definition-diagnostics.js +353 -0
- package/dist/definition-diagnostics.js.map +1 -0
- package/dist/definition-diff.d.ts +51 -0
- package/dist/definition-diff.d.ts.map +1 -0
- package/dist/definition-diff.js +172 -0
- package/dist/definition-diff.js.map +1 -0
- package/dist/definition-json.d.ts +53 -0
- package/dist/definition-json.d.ts.map +1 -0
- package/dist/definition-json.js +186 -0
- package/dist/definition-json.js.map +1 -0
- package/dist/definition-manifest.d.ts +41 -0
- package/dist/definition-manifest.d.ts.map +1 -0
- package/dist/definition-manifest.js +119 -0
- package/dist/definition-manifest.js.map +1 -0
- package/dist/definition-package-execution-json.d.ts +64 -0
- package/dist/definition-package-execution-json.d.ts.map +1 -0
- package/dist/definition-package-execution-json.js +245 -0
- package/dist/definition-package-execution-json.js.map +1 -0
- package/dist/definition-package-execution-replay.d.ts +14 -0
- package/dist/definition-package-execution-replay.d.ts.map +1 -0
- package/dist/definition-package-execution-replay.js +47 -0
- package/dist/definition-package-execution-replay.js.map +1 -0
- package/dist/definition-package-executor.d.ts +52 -0
- package/dist/definition-package-executor.d.ts.map +1 -0
- package/dist/definition-package-executor.js +70 -0
- package/dist/definition-package-executor.js.map +1 -0
- package/dist/definition-package-loader.d.ts +36 -0
- package/dist/definition-package-loader.d.ts.map +1 -0
- package/dist/definition-package-loader.js +56 -0
- package/dist/definition-package-loader.js.map +1 -0
- package/dist/definition-package-replay.d.ts +28 -0
- package/dist/definition-package-replay.d.ts.map +1 -0
- package/dist/definition-package-replay.js +74 -0
- package/dist/definition-package-replay.js.map +1 -0
- package/dist/definition-package-resolver.d.ts +31 -0
- package/dist/definition-package-resolver.d.ts.map +1 -0
- package/dist/definition-package-resolver.js +60 -0
- package/dist/definition-package-resolver.js.map +1 -0
- package/dist/definition-package-review.d.ts +29 -0
- package/dist/definition-package-review.d.ts.map +1 -0
- package/dist/definition-package-review.js +74 -0
- package/dist/definition-package-review.js.map +1 -0
- package/dist/definition-package-run-report.d.ts +25 -0
- package/dist/definition-package-run-report.d.ts.map +1 -0
- package/dist/definition-package-run-report.js +37 -0
- package/dist/definition-package-run-report.js.map +1 -0
- package/dist/definition-package-run.d.ts +20 -0
- package/dist/definition-package-run.d.ts.map +1 -0
- package/dist/definition-package-run.js +41 -0
- package/dist/definition-package-run.js.map +1 -0
- package/dist/definition-package-selection.d.ts +29 -0
- package/dist/definition-package-selection.d.ts.map +1 -0
- package/dist/definition-package-selection.js +68 -0
- package/dist/definition-package-selection.js.map +1 -0
- package/dist/definition-package-smoke.d.ts +74 -0
- package/dist/definition-package-smoke.d.ts.map +1 -0
- package/dist/definition-package-smoke.js +184 -0
- package/dist/definition-package-smoke.js.map +1 -0
- package/dist/definition-package.d.ts +36 -0
- package/dist/definition-package.d.ts.map +1 -0
- package/dist/definition-package.js +114 -0
- package/dist/definition-package.js.map +1 -0
- package/dist/definition-tuning-approval.d.ts +39 -0
- package/dist/definition-tuning-approval.d.ts.map +1 -0
- package/dist/definition-tuning-approval.js +207 -0
- package/dist/definition-tuning-approval.js.map +1 -0
- package/dist/definition-tuning-json.d.ts +58 -0
- package/dist/definition-tuning-json.d.ts.map +1 -0
- package/dist/definition-tuning-json.js +276 -0
- package/dist/definition-tuning-json.js.map +1 -0
- package/dist/definition-tuning-promotion.d.ts +120 -0
- package/dist/definition-tuning-promotion.d.ts.map +1 -0
- package/dist/definition-tuning-promotion.js +460 -0
- package/dist/definition-tuning-promotion.js.map +1 -0
- package/dist/definition-tuning-replay.d.ts +14 -0
- package/dist/definition-tuning-replay.d.ts.map +1 -0
- package/dist/definition-tuning-replay.js +50 -0
- package/dist/definition-tuning-replay.js.map +1 -0
- package/dist/definition-tuning-review-artifact.d.ts +56 -0
- package/dist/definition-tuning-review-artifact.d.ts.map +1 -0
- package/dist/definition-tuning-review-artifact.js +302 -0
- package/dist/definition-tuning-review-artifact.js.map +1 -0
- package/dist/definition-tuning-review.d.ts +59 -0
- package/dist/definition-tuning-review.d.ts.map +1 -0
- package/dist/definition-tuning-review.js +213 -0
- package/dist/definition-tuning-review.js.map +1 -0
- package/dist/definition-tuning-source-patch.d.ts +43 -0
- package/dist/definition-tuning-source-patch.d.ts.map +1 -0
- package/dist/definition-tuning-source-patch.js +214 -0
- package/dist/definition-tuning-source-patch.js.map +1 -0
- package/dist/definition-tuning.d.ts +73 -0
- package/dist/definition-tuning.d.ts.map +1 -0
- package/dist/definition-tuning.js +426 -0
- package/dist/definition-tuning.js.map +1 -0
- package/dist/definition.d.ts +189 -0
- package/dist/definition.d.ts.map +1 -0
- package/dist/definition.js +494 -0
- package/dist/definition.js.map +1 -0
- package/dist/direct-route-invocation.d.ts +132 -0
- package/dist/direct-route-invocation.d.ts.map +1 -0
- package/dist/direct-route-invocation.js +325 -0
- package/dist/direct-route-invocation.js.map +1 -0
- package/dist/direct-route-policy.d.ts +25 -0
- package/dist/direct-route-policy.d.ts.map +1 -0
- package/dist/direct-route-policy.js +106 -0
- package/dist/direct-route-policy.js.map +1 -0
- package/dist/eval-case.d.ts +85 -0
- package/dist/eval-case.d.ts.map +1 -0
- package/dist/eval-case.js +366 -0
- package/dist/eval-case.js.map +1 -0
- package/dist/eval-replay.d.ts +18 -0
- package/dist/eval-replay.d.ts.map +1 -0
- package/dist/eval-replay.js +41 -0
- package/dist/eval-replay.js.map +1 -0
- package/dist/eval-result.d.ts +46 -0
- package/dist/eval-result.d.ts.map +1 -0
- package/dist/eval-result.js +183 -0
- package/dist/eval-result.js.map +1 -0
- package/dist/eval-runner.d.ts +30 -0
- package/dist/eval-runner.d.ts.map +1 -0
- package/dist/eval-runner.js +57 -0
- package/dist/eval-runner.js.map +1 -0
- package/dist/eval-suite.d.ts +43 -0
- package/dist/eval-suite.d.ts.map +1 -0
- package/dist/eval-suite.js +101 -0
- package/dist/eval-suite.js.map +1 -0
- package/dist/execution-plan.d.ts +43 -0
- package/dist/execution-plan.d.ts.map +1 -0
- package/dist/execution-plan.js +187 -0
- package/dist/execution-plan.js.map +1 -0
- package/dist/hosted-tool-policy.d.ts +70 -0
- package/dist/hosted-tool-policy.d.ts.map +1 -0
- package/dist/hosted-tool-policy.js +228 -0
- package/dist/hosted-tool-policy.js.map +1 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/memory-policy.d.ts +55 -0
- package/dist/memory-policy.d.ts.map +1 -0
- package/dist/memory-policy.js +187 -0
- package/dist/memory-policy.js.map +1 -0
- package/dist/memory-updates.d.ts +39 -0
- package/dist/memory-updates.d.ts.map +1 -0
- package/dist/memory-updates.js +110 -0
- package/dist/memory-updates.js.map +1 -0
- package/dist/outcome-trace.d.ts +79 -0
- package/dist/outcome-trace.d.ts.map +1 -0
- package/dist/outcome-trace.js +117 -0
- package/dist/outcome-trace.js.map +1 -0
- package/dist/outcome.d.ts +97 -0
- package/dist/outcome.d.ts.map +1 -0
- package/dist/outcome.js +160 -0
- package/dist/outcome.js.map +1 -0
- package/dist/planner-compiler.d.ts +14 -0
- package/dist/planner-compiler.d.ts.map +1 -0
- package/dist/planner-compiler.js +26 -0
- package/dist/planner-compiler.js.map +1 -0
- package/dist/planning.d.ts +36 -0
- package/dist/planning.d.ts.map +1 -0
- package/dist/planning.js +141 -0
- package/dist/planning.js.map +1 -0
- package/dist/policy-resolution.d.ts +10 -0
- package/dist/policy-resolution.d.ts.map +1 -0
- package/dist/policy-resolution.js +30 -0
- package/dist/policy-resolution.js.map +1 -0
- package/dist/run-evaluation.d.ts +29 -0
- package/dist/run-evaluation.d.ts.map +1 -0
- package/dist/run-evaluation.js +128 -0
- package/dist/run-evaluation.js.map +1 -0
- package/dist/run-parity.d.ts +24 -0
- package/dist/run-parity.d.ts.map +1 -0
- package/dist/run-parity.js +36 -0
- package/dist/run-parity.js.map +1 -0
- package/dist/run-report-batch.d.ts +150 -0
- package/dist/run-report-batch.d.ts.map +1 -0
- package/dist/run-report-batch.js +391 -0
- package/dist/run-report-batch.js.map +1 -0
- package/dist/run-report-json.d.ts +76 -0
- package/dist/run-report-json.d.ts.map +1 -0
- package/dist/run-report-json.js +451 -0
- package/dist/run-report-json.js.map +1 -0
- package/dist/run-report.d.ts +22 -0
- package/dist/run-report.d.ts.map +1 -0
- package/dist/run-report.js +46 -0
- package/dist/run-report.js.map +1 -0
- package/dist/run.d.ts +20 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +36 -0
- package/dist/run.js.map +1 -0
- package/dist/tool-bindings.d.ts +42 -0
- package/dist/tool-bindings.d.ts.map +1 -0
- package/dist/tool-bindings.js +124 -0
- package/dist/tool-bindings.js.map +1 -0
- package/dist/tool-capabilities.d.ts +37 -0
- package/dist/tool-capabilities.d.ts.map +1 -0
- package/dist/tool-capabilities.js +91 -0
- package/dist/tool-capabilities.js.map +1 -0
- package/dist/tool-executor.d.ts +63 -0
- package/dist/tool-executor.d.ts.map +1 -0
- package/dist/tool-executor.js +176 -0
- package/dist/tool-executor.js.map +1 -0
- package/dist/tool-fixtures.d.ts +18 -0
- package/dist/tool-fixtures.d.ts.map +1 -0
- package/dist/tool-fixtures.js +52 -0
- package/dist/tool-fixtures.js.map +1 -0
- package/dist/tool-invocations.d.ts +31 -0
- package/dist/tool-invocations.d.ts.map +1 -0
- package/dist/tool-invocations.js +79 -0
- package/dist/tool-invocations.js.map +1 -0
- package/dist/tool-provider-executors.d.ts +43 -0
- package/dist/tool-provider-executors.d.ts.map +1 -0
- package/dist/tool-provider-executors.js +59 -0
- package/dist/tool-provider-executors.js.map +1 -0
- package/dist/tool-result-cache.d.ts +37 -0
- package/dist/tool-result-cache.d.ts.map +1 -0
- package/dist/tool-result-cache.js +173 -0
- package/dist/tool-result-cache.js.map +1 -0
- package/dist/tool-results.d.ts +41 -0
- package/dist/tool-results.d.ts.map +1 -0
- package/dist/tool-results.js +137 -0
- package/dist/tool-results.js.map +1 -0
- package/dist/tool-runtime.d.ts +46 -0
- package/dist/tool-runtime.d.ts.map +1 -0
- package/dist/tool-runtime.js +163 -0
- package/dist/tool-runtime.js.map +1 -0
- package/dist/tool-selection-evaluation.d.ts +214 -0
- package/dist/tool-selection-evaluation.d.ts.map +1 -0
- package/dist/tool-selection-evaluation.js +773 -0
- package/dist/tool-selection-evaluation.js.map +1 -0
- package/dist/tool-selection-json.d.ts +71 -0
- package/dist/tool-selection-json.d.ts.map +1 -0
- package/dist/tool-selection-json.js +284 -0
- package/dist/tool-selection-json.js.map +1 -0
- package/dist/tool-selection-policy.d.ts +81 -0
- package/dist/tool-selection-policy.d.ts.map +1 -0
- package/dist/tool-selection-policy.js +376 -0
- package/dist/tool-selection-policy.js.map +1 -0
- package/dist/tool-selection-replay.d.ts +26 -0
- package/dist/tool-selection-replay.d.ts.map +1 -0
- package/dist/tool-selection-replay.js +88 -0
- package/dist/tool-selection-replay.js.map +1 -0
- package/dist/tool-selection-rules.d.ts +46 -0
- package/dist/tool-selection-rules.d.ts.map +1 -0
- package/dist/tool-selection-rules.js +183 -0
- package/dist/tool-selection-rules.js.map +1 -0
- package/dist/tool-selection-trace-export.d.ts +37 -0
- package/dist/tool-selection-trace-export.d.ts.map +1 -0
- package/dist/tool-selection-trace-export.js +324 -0
- package/dist/tool-selection-trace-export.js.map +1 -0
- package/dist/trace-events.d.ts +14 -0
- package/dist/trace-events.d.ts.map +1 -0
- package/dist/trace-events.js +65 -0
- package/dist/trace-events.js.map +1 -0
- package/dist/trace-payload.d.ts +24 -0
- package/dist/trace-payload.d.ts.map +1 -0
- package/dist/trace-payload.js +113 -0
- package/dist/trace-payload.js.map +1 -0
- package/dist/trace-replay.d.ts +276 -0
- package/dist/trace-replay.d.ts.map +1 -0
- package/dist/trace-replay.js +685 -0
- package/dist/trace-replay.js.map +1 -0
- package/dist/trace-report.d.ts +320 -0
- package/dist/trace-report.d.ts.map +1 -0
- package/dist/trace-report.js +1394 -0
- package/dist/trace-report.js.map +1 -0
- package/dist/trace-stages.d.ts +70 -0
- package/dist/trace-stages.d.ts.map +1 -0
- package/dist/trace-stages.js +103 -0
- package/dist/trace-stages.js.map +1 -0
- package/dist/value-readers.d.ts +20 -0
- package/dist/value-readers.d.ts.map +1 -0
- package/dist/value-readers.js +82 -0
- package/dist/value-readers.js.map +1 -0
- package/package.json +341 -0
|
@@ -0,0 +1,1394 @@
|
|
|
1
|
+
import { buildAssistantWorkflowTraceEvalCandidate, buildAssistantWorkflowTraceSummary, collectAssistantWorkflowTraceTuningSignals, getAssistantWorkflowUnmodeledToolCalls, getMissingWorkflowSelectionCandidateTools, hasAssistantWorkflowConstrainedToolSelectionEvidence, } from "./trace-replay.js";
|
|
2
|
+
import { readAssistantWorkflowTraceString as normalizeNullableString, } from "./trace-events.js";
|
|
3
|
+
import { ASSISTANT_WORKFLOW_RUN_REPORT_JSON_SCHEMA_VERSION, } from "./run-report-json.js";
|
|
4
|
+
import { ASSISTANT_WORKFLOW_TRACE_STAGE } from "./trace-stages.js";
|
|
5
|
+
import { buildAssistantWorkflowToolSelectionProfileKey } from "./tool-selection-policy.js";
|
|
6
|
+
export function buildAssistantWorkflowMemoryPatchReadiness(report, options = {}) {
|
|
7
|
+
const workflowIds = normalizeStringScope(options.workflowIds);
|
|
8
|
+
const workflowVersions = normalizeNumberScope(options.workflowVersions);
|
|
9
|
+
const scopedRows = selectWorkflowScopedTraceRows(report.rows, workflowIds, workflowVersions);
|
|
10
|
+
const scopedPatches = scopedRows.flatMap((row) => row.summary.memoryPatches);
|
|
11
|
+
const minEquivalentPatchCount = normalizePositiveInt(options.minEquivalentPatchCount, 1);
|
|
12
|
+
const minGuardedWorkflowWriteCount = normalizeNonNegativeInt(options.minGuardedWorkflowWriteCount, 0);
|
|
13
|
+
const minWorkflowPatchWriteCount = normalizeNonNegativeInt(options.minWorkflowPatchWriteCount, 0);
|
|
14
|
+
const minWorkflowModePatchWriteCount = normalizeNonNegativeInt(options.minWorkflowModePatchWriteCount, 0);
|
|
15
|
+
const requiredPatchDerivationCounts = normalizePatchDerivationRequirements(options.minPatchDerivationCounts);
|
|
16
|
+
const equivalenceCounts = buildStringCounts(scopedPatches.map(buildMemoryPatchEquivalenceKey));
|
|
17
|
+
const derivationCounts = buildStringCounts(scopedPatches.map(buildMemoryPatchDerivationKey));
|
|
18
|
+
const mismatchedProfileFieldCounts = buildStringCounts(scopedPatches.flatMap((patch) => patch.profilePatchMismatchedFields));
|
|
19
|
+
const writeSourceCounts = buildStringCounts(scopedPatches.map(buildMemoryPatchWriteSourceKey));
|
|
20
|
+
const writeReasonCounts = buildStringCounts(scopedPatches.map(buildMemoryPatchWriteReasonKey));
|
|
21
|
+
const equivalentPatchCount = findReportCount(equivalenceCounts, "equivalent");
|
|
22
|
+
const mismatchPatchCount = findReportCount(equivalenceCounts, "mismatch");
|
|
23
|
+
const unknownPatchCount = findReportCount(equivalenceCounts, "unknown");
|
|
24
|
+
const totalPatchCount = equivalenceCounts.reduce((sum, item) => sum + item.count, 0);
|
|
25
|
+
const guardedWorkflowWriteCount = findReportCount(writeSourceCounts, "guarded:workflow_patch");
|
|
26
|
+
const workflowModePatchWriteCount = findReportCount(writeSourceCounts, "workflow:workflow_patch");
|
|
27
|
+
const workflowPatchWriteCount = sumWriteSourceCountsBySource(writeSourceCounts, "workflow_patch");
|
|
28
|
+
const guardedLegacyFallbackCount = findReportCount(writeSourceCounts, "guarded:legacy_fallback");
|
|
29
|
+
const blockingReasons = [];
|
|
30
|
+
if (equivalentPatchCount < minEquivalentPatchCount) {
|
|
31
|
+
blockingReasons.push(`equivalent_patch_count_below_minimum:${equivalentPatchCount}/${minEquivalentPatchCount}`);
|
|
32
|
+
}
|
|
33
|
+
if (mismatchPatchCount > 0) {
|
|
34
|
+
blockingReasons.push(`profile_patch_mismatch:${mismatchPatchCount}`);
|
|
35
|
+
}
|
|
36
|
+
if (unknownPatchCount > 0) {
|
|
37
|
+
blockingReasons.push(`profile_patch_equivalence_unknown:${unknownPatchCount}`);
|
|
38
|
+
}
|
|
39
|
+
if (guardedLegacyFallbackCount > 0) {
|
|
40
|
+
blockingReasons.push(`guarded_legacy_fallback:${guardedLegacyFallbackCount}`);
|
|
41
|
+
}
|
|
42
|
+
if (guardedWorkflowWriteCount < minGuardedWorkflowWriteCount) {
|
|
43
|
+
blockingReasons.push(`guarded_workflow_write_count_below_minimum:${guardedWorkflowWriteCount}/${minGuardedWorkflowWriteCount}`);
|
|
44
|
+
}
|
|
45
|
+
if (workflowPatchWriteCount < minWorkflowPatchWriteCount) {
|
|
46
|
+
blockingReasons.push(`workflow_patch_write_count_below_minimum:${workflowPatchWriteCount}/${minWorkflowPatchWriteCount}`);
|
|
47
|
+
}
|
|
48
|
+
if (workflowModePatchWriteCount < minWorkflowModePatchWriteCount) {
|
|
49
|
+
blockingReasons.push(`workflow_mode_patch_write_count_below_minimum:${workflowModePatchWriteCount}/${minWorkflowModePatchWriteCount}`);
|
|
50
|
+
}
|
|
51
|
+
for (const requirement of requiredPatchDerivationCounts) {
|
|
52
|
+
const observedCount = findReportCount(derivationCounts, requirement.key);
|
|
53
|
+
if (observedCount < requirement.count) {
|
|
54
|
+
blockingReasons.push(`patch_derivation_count_below_minimum:${requirement.key}:${observedCount}/${requirement.count}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const status = mismatchPatchCount > 0 || unknownPatchCount > 0 || guardedLegacyFallbackCount > 0
|
|
58
|
+
? "blocked"
|
|
59
|
+
: blockingReasons.length > 0
|
|
60
|
+
? "insufficient_evidence"
|
|
61
|
+
: "ready";
|
|
62
|
+
return {
|
|
63
|
+
status,
|
|
64
|
+
ready: status === "ready",
|
|
65
|
+
workflowIds,
|
|
66
|
+
workflowVersions,
|
|
67
|
+
scopedRowCount: scopedRows.length,
|
|
68
|
+
minEquivalentPatchCount,
|
|
69
|
+
equivalentPatchCount,
|
|
70
|
+
mismatchPatchCount,
|
|
71
|
+
unknownPatchCount,
|
|
72
|
+
totalPatchCount,
|
|
73
|
+
guardedWorkflowWriteCount,
|
|
74
|
+
workflowModePatchWriteCount,
|
|
75
|
+
minWorkflowModePatchWriteCount,
|
|
76
|
+
workflowPatchWriteCount,
|
|
77
|
+
minWorkflowPatchWriteCount,
|
|
78
|
+
guardedLegacyFallbackCount,
|
|
79
|
+
patchDerivationCounts: derivationCounts,
|
|
80
|
+
requiredPatchDerivationCounts,
|
|
81
|
+
mismatchedProfileFieldCounts,
|
|
82
|
+
writeSourceCounts,
|
|
83
|
+
writeReasonCounts,
|
|
84
|
+
blockingReasons,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
export function buildAssistantWorkflowIntentMemoryPolicyReadiness(report, options = {}) {
|
|
88
|
+
const workflowIds = normalizeStringScope(options.workflowIds);
|
|
89
|
+
const workflowVersions = normalizeNumberScope(options.workflowVersions);
|
|
90
|
+
const scopedRows = selectWorkflowScopedTraceRows(report.rows, workflowIds, workflowVersions);
|
|
91
|
+
const scopedIntentPolicies = scopedRows.flatMap((row) => row.summary.memoryPatches.flatMap((patch) => patch.intentWritePolicy ? [patch.intentWritePolicy] : []));
|
|
92
|
+
const minObservedPolicyCount = normalizePositiveInt(options.minObservedPolicyCount, 3);
|
|
93
|
+
const minEquivalentPolicyCount = options.minEquivalentPolicyCount === null || options.minEquivalentPolicyCount === undefined
|
|
94
|
+
? minObservedPolicyCount
|
|
95
|
+
: normalizeNonNegativeInt(options.minEquivalentPolicyCount, minObservedPolicyCount);
|
|
96
|
+
const maxMismatchPolicyCount = normalizeNonNegativeInt(options.maxMismatchPolicyCount, 0);
|
|
97
|
+
const maxUnknownPolicyCount = normalizeNonNegativeInt(options.maxUnknownPolicyCount, 0);
|
|
98
|
+
const maxWriteNotAllowedCount = normalizeNonNegativeInt(options.maxWriteNotAllowedCount, 0);
|
|
99
|
+
const intentCounts = buildStringCounts(scopedIntentPolicies.flatMap((policy) => policy.intentId ? [policy.intentId] : []));
|
|
100
|
+
const equivalenceCounts = buildStringCounts(scopedIntentPolicies.map(buildMemoryPatchEquivalenceKey));
|
|
101
|
+
const skippedPatchReasonCounts = buildStringCounts(scopedIntentPolicies.flatMap((policy) => policy.skippedPatches.map(buildMemorySkippedPatchReasonKey)));
|
|
102
|
+
const mismatchedProfileFieldCounts = buildStringCounts(scopedIntentPolicies.flatMap((policy) => policy.profilePatchMismatchedFields));
|
|
103
|
+
const equivalentPolicyCount = findReportCount(equivalenceCounts, "equivalent");
|
|
104
|
+
const mismatchPolicyCount = findReportCount(equivalenceCounts, "mismatch");
|
|
105
|
+
const unknownPolicyCount = findReportCount(equivalenceCounts, "unknown");
|
|
106
|
+
const observedPolicyCount = equivalenceCounts
|
|
107
|
+
.reduce((sum, item) => sum + item.count, 0);
|
|
108
|
+
const writeNotAllowedCount = skippedPatchReasonCounts
|
|
109
|
+
.filter((item) => item.key.endsWith(":write-not-allowed"))
|
|
110
|
+
.reduce((sum, item) => sum + item.count, 0);
|
|
111
|
+
const blockingReasons = [];
|
|
112
|
+
if (mismatchPolicyCount > maxMismatchPolicyCount) {
|
|
113
|
+
blockingReasons.push(`intent_write_policy_mismatch:${mismatchPolicyCount}/${maxMismatchPolicyCount}`);
|
|
114
|
+
}
|
|
115
|
+
if (unknownPolicyCount > maxUnknownPolicyCount) {
|
|
116
|
+
blockingReasons.push(`intent_write_policy_equivalence_unknown:${unknownPolicyCount}/${maxUnknownPolicyCount}`);
|
|
117
|
+
}
|
|
118
|
+
if (writeNotAllowedCount > maxWriteNotAllowedCount) {
|
|
119
|
+
blockingReasons.push(`intent_write_policy_write_not_allowed:${writeNotAllowedCount}/${maxWriteNotAllowedCount}`);
|
|
120
|
+
}
|
|
121
|
+
if (observedPolicyCount < minObservedPolicyCount) {
|
|
122
|
+
blockingReasons.push(`intent_write_policy_observed_count_below_minimum:${observedPolicyCount}/${minObservedPolicyCount}`);
|
|
123
|
+
}
|
|
124
|
+
if (equivalentPolicyCount < minEquivalentPolicyCount) {
|
|
125
|
+
blockingReasons.push(`intent_write_policy_equivalent_count_below_minimum:${equivalentPolicyCount}/${minEquivalentPolicyCount}`);
|
|
126
|
+
}
|
|
127
|
+
const hardBlock = mismatchPolicyCount > maxMismatchPolicyCount ||
|
|
128
|
+
unknownPolicyCount > maxUnknownPolicyCount ||
|
|
129
|
+
writeNotAllowedCount > maxWriteNotAllowedCount;
|
|
130
|
+
const status = hardBlock
|
|
131
|
+
? "blocked"
|
|
132
|
+
: blockingReasons.length > 0
|
|
133
|
+
? "insufficient_evidence"
|
|
134
|
+
: "ready";
|
|
135
|
+
return {
|
|
136
|
+
status,
|
|
137
|
+
ready: status === "ready",
|
|
138
|
+
workflowIds,
|
|
139
|
+
workflowVersions,
|
|
140
|
+
scopedRowCount: scopedRows.length,
|
|
141
|
+
minObservedPolicyCount,
|
|
142
|
+
minEquivalentPolicyCount,
|
|
143
|
+
maxMismatchPolicyCount,
|
|
144
|
+
maxUnknownPolicyCount,
|
|
145
|
+
maxWriteNotAllowedCount,
|
|
146
|
+
observedPolicyCount,
|
|
147
|
+
equivalentPolicyCount,
|
|
148
|
+
mismatchPolicyCount,
|
|
149
|
+
unknownPolicyCount,
|
|
150
|
+
writeNotAllowedCount,
|
|
151
|
+
intentCounts,
|
|
152
|
+
mismatchedProfileFieldCounts,
|
|
153
|
+
skippedPatchReasonCounts,
|
|
154
|
+
blockingReasons,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
export function buildAssistantWorkflowToolSelectionMetricReadiness(report, options = {}) {
|
|
158
|
+
const workflowIds = normalizeStringScope(options.workflowIds);
|
|
159
|
+
const workflowVersions = normalizeNumberScope(options.workflowVersions);
|
|
160
|
+
const workflowProfileKeys = normalizeStringScope(options.workflowProfileKeys);
|
|
161
|
+
const authoritySources = normalizeStringScope(options.authoritySources);
|
|
162
|
+
const intentIds = normalizeStringScope(options.intentIds);
|
|
163
|
+
const segmentKeys = normalizeStringScope(options.segmentKeys);
|
|
164
|
+
const requiredSchemaCostSource = normalizeOptionalString(options.schemaCostSource);
|
|
165
|
+
const minSelectionCount = normalizePositiveInt(options.minSelectionCount, 3);
|
|
166
|
+
const maxQualityFlaggedSelectionCount = normalizeNonNegativeInt(options.maxQualityFlaggedSelectionCount, 0);
|
|
167
|
+
const maxErrorSelectionCount = normalizeNonNegativeInt(options.maxErrorSelectionCount, 0);
|
|
168
|
+
const maxBadFeedbackSelectionCount = normalizeNonNegativeInt(options.maxBadFeedbackSelectionCount, 0);
|
|
169
|
+
const maxUnavailableOutcomeSelectionCount = normalizeNonNegativeInt(options.maxUnavailableOutcomeSelectionCount, 0);
|
|
170
|
+
const scopedSegments = report.effectiveToolSelectionSegments.filter((segment) => isToolSelectionMetricSegmentInScope(segment, workflowIds, workflowVersions, workflowProfileKeys, authoritySources, intentIds, segmentKeys));
|
|
171
|
+
const ignoredSchemaCostSourceSegments = requiredSchemaCostSource
|
|
172
|
+
? scopedSegments.filter((segment) => segment.schemaCostSource !== requiredSchemaCostSource)
|
|
173
|
+
: [];
|
|
174
|
+
const evaluatedSegments = requiredSchemaCostSource
|
|
175
|
+
? scopedSegments.filter((segment) => segment.schemaCostSource === requiredSchemaCostSource)
|
|
176
|
+
: scopedSegments;
|
|
177
|
+
const evidenceCounters = buildToolSelectionMetricEvidenceCounters(report.rows);
|
|
178
|
+
const segments = evaluatedSegments.map((segment) => {
|
|
179
|
+
const counters = evidenceCounters.get(segment.segmentKey) ?? EMPTY_TOOL_SELECTION_METRIC_EVIDENCE_COUNTERS;
|
|
180
|
+
const blockingReasons = [];
|
|
181
|
+
if (counters.qualityFlaggedSelectionCount > maxQualityFlaggedSelectionCount) {
|
|
182
|
+
blockingReasons.push(`quality_flagged_selection_count_exceeds_maximum:${counters.qualityFlaggedSelectionCount}/${maxQualityFlaggedSelectionCount}`);
|
|
183
|
+
}
|
|
184
|
+
if (counters.errorSelectionCount > maxErrorSelectionCount) {
|
|
185
|
+
blockingReasons.push(`error_selection_count_exceeds_maximum:${counters.errorSelectionCount}/${maxErrorSelectionCount}`);
|
|
186
|
+
}
|
|
187
|
+
if (counters.badFeedbackSelectionCount > maxBadFeedbackSelectionCount) {
|
|
188
|
+
blockingReasons.push(`bad_feedback_selection_count_exceeds_maximum:${counters.badFeedbackSelectionCount}/${maxBadFeedbackSelectionCount}`);
|
|
189
|
+
}
|
|
190
|
+
if (counters.unavailableOutcomeSelectionCount > maxUnavailableOutcomeSelectionCount) {
|
|
191
|
+
blockingReasons.push(`unavailable_outcome_selection_count_exceeds_maximum:${counters.unavailableOutcomeSelectionCount}/${maxUnavailableOutcomeSelectionCount}`);
|
|
192
|
+
}
|
|
193
|
+
if (segment.selectionCount < minSelectionCount) {
|
|
194
|
+
blockingReasons.push(`selection_count_below_minimum:${segment.selectionCount}/${minSelectionCount}`);
|
|
195
|
+
}
|
|
196
|
+
const hardBlock = counters.qualityFlaggedSelectionCount > maxQualityFlaggedSelectionCount ||
|
|
197
|
+
counters.errorSelectionCount > maxErrorSelectionCount ||
|
|
198
|
+
counters.badFeedbackSelectionCount > maxBadFeedbackSelectionCount ||
|
|
199
|
+
counters.unavailableOutcomeSelectionCount > maxUnavailableOutcomeSelectionCount;
|
|
200
|
+
const status = hardBlock
|
|
201
|
+
? "blocked"
|
|
202
|
+
: segment.selectionCount < minSelectionCount
|
|
203
|
+
? "insufficient_evidence"
|
|
204
|
+
: "ready_for_review";
|
|
205
|
+
return {
|
|
206
|
+
...segment,
|
|
207
|
+
...counters,
|
|
208
|
+
status,
|
|
209
|
+
readyForReview: status === "ready_for_review",
|
|
210
|
+
blockingReasons,
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
const readySegmentCount = segments.filter((segment) => segment.status === "ready_for_review").length;
|
|
214
|
+
const blockedSegmentCount = segments.filter((segment) => segment.status === "blocked").length;
|
|
215
|
+
const insufficientEvidenceSegmentCount = segments.filter((segment) => segment.status === "insufficient_evidence").length;
|
|
216
|
+
const blockingReasons = [];
|
|
217
|
+
if (!segments.length) {
|
|
218
|
+
blockingReasons.push("no_tool_selection_segments_in_scope");
|
|
219
|
+
}
|
|
220
|
+
if (blockedSegmentCount > 0) {
|
|
221
|
+
blockingReasons.push(`blocked_segments:${blockedSegmentCount}`);
|
|
222
|
+
}
|
|
223
|
+
if (insufficientEvidenceSegmentCount > 0) {
|
|
224
|
+
blockingReasons.push(`insufficient_evidence_segments:${insufficientEvidenceSegmentCount}`);
|
|
225
|
+
}
|
|
226
|
+
const status = blockedSegmentCount > 0
|
|
227
|
+
? "blocked"
|
|
228
|
+
: !segments.length || insufficientEvidenceSegmentCount > 0
|
|
229
|
+
? "insufficient_evidence"
|
|
230
|
+
: "ready_for_review";
|
|
231
|
+
return {
|
|
232
|
+
status,
|
|
233
|
+
readyForReview: status === "ready_for_review",
|
|
234
|
+
workflowIds,
|
|
235
|
+
workflowVersions,
|
|
236
|
+
workflowProfileKeys,
|
|
237
|
+
authoritySources,
|
|
238
|
+
intentIds,
|
|
239
|
+
segmentKeys,
|
|
240
|
+
requiredSchemaCostSource,
|
|
241
|
+
minSelectionCount,
|
|
242
|
+
maxQualityFlaggedSelectionCount,
|
|
243
|
+
maxErrorSelectionCount,
|
|
244
|
+
maxBadFeedbackSelectionCount,
|
|
245
|
+
maxUnavailableOutcomeSelectionCount,
|
|
246
|
+
evaluatedSelectionCount: segments.reduce((sum, segment) => sum + segment.selectionCount, 0),
|
|
247
|
+
readySegmentCount,
|
|
248
|
+
blockedSegmentCount,
|
|
249
|
+
insufficientEvidenceSegmentCount,
|
|
250
|
+
ignoredSchemaCostSourceSegmentCount: ignoredSchemaCostSourceSegments.length,
|
|
251
|
+
ignoredSchemaCostSourceSelectionCount: ignoredSchemaCostSourceSegments
|
|
252
|
+
.reduce((sum, segment) => sum + segment.selectionCount, 0),
|
|
253
|
+
ignoredSchemaCostSourceCounts: buildStringCounts(ignoredSchemaCostSourceSegments.map((segment) => segment.schemaCostSource ?? "unknown")),
|
|
254
|
+
segments,
|
|
255
|
+
blockingReasons,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
export function buildAssistantWorkflowTraceReport(sourceRows, options = {}) {
|
|
259
|
+
const directRouteDefinitionLookup = buildDirectRouteDefinitionLookup(options.workflow ?? null);
|
|
260
|
+
const workflowIds = normalizeStringScope(options.workflowIds);
|
|
261
|
+
const workflowVersions = normalizeNumberScope(options.workflowVersions);
|
|
262
|
+
const allRows = sourceRows.map((row) => {
|
|
263
|
+
const summary = buildAssistantWorkflowTraceSummary(row.traceEvents);
|
|
264
|
+
const tuningSignals = collectAssistantWorkflowTraceTuningSignals(summary);
|
|
265
|
+
return {
|
|
266
|
+
debugId: normalizeNullableString(row.debugId),
|
|
267
|
+
requestId: row.requestId,
|
|
268
|
+
threadId: normalizeNullableString(row.threadId),
|
|
269
|
+
userId: normalizeNullableString(row.userId),
|
|
270
|
+
provider: normalizeNullableString(row.provider),
|
|
271
|
+
model: normalizeNullableString(row.model),
|
|
272
|
+
feedback: normalizeNullableString(row.feedback),
|
|
273
|
+
feedbackAt: normalizeNullableString(row.feedbackAt),
|
|
274
|
+
createdAt: normalizeNullableString(row.createdAt),
|
|
275
|
+
summary,
|
|
276
|
+
evalCandidate: buildAssistantWorkflowTraceEvalCandidate(summary),
|
|
277
|
+
tuningSignals,
|
|
278
|
+
directRouteStrategies: buildDirectRouteStrategySummaries(summary, directRouteDefinitionLookup),
|
|
279
|
+
};
|
|
280
|
+
});
|
|
281
|
+
const workflowScopedRows = selectWorkflowScopedTraceRows(allRows, workflowIds, workflowVersions);
|
|
282
|
+
const rows = options.onlyInteresting === true
|
|
283
|
+
? workflowScopedRows.filter((row) => isInterestingReportRow(row))
|
|
284
|
+
: workflowScopedRows;
|
|
285
|
+
return {
|
|
286
|
+
generatedAt: new Date().toISOString(),
|
|
287
|
+
workflowIds,
|
|
288
|
+
workflowVersions,
|
|
289
|
+
rowCount: rows.length,
|
|
290
|
+
scopedSourceRowCount: workflowScopedRows.length,
|
|
291
|
+
sourceRowCount: sourceRows.length,
|
|
292
|
+
evalCandidateCount: rows.filter((row) => row.evalCandidate !== null).length,
|
|
293
|
+
interestingRowCount: rows.filter(isInterestingReportRow).length,
|
|
294
|
+
qualityFlaggedRowCount: rows.filter((row) => row.summary.quality.flagged).length,
|
|
295
|
+
errorRowCount: rows.filter((row) => row.summary.errorStages.length > 0).length,
|
|
296
|
+
feedbackCounts: buildStringCounts(rows.map((row) => row.feedback ?? "none")),
|
|
297
|
+
intentCounts: buildStringCounts(rows.map((row) => row.summary.classifier?.intent ?? row.summary.workflow?.intent ?? "unknown")),
|
|
298
|
+
authorityCounts: buildStringCounts(rows.map((row) => row.summary.workflow?.authority?.source ?? "none")),
|
|
299
|
+
authorityReasonCounts: buildStringCounts(rows.map((row) => row.summary.workflow?.authority?.reason ?? "none")),
|
|
300
|
+
workflowSelectionOptionalToolModeCounts: buildStringCounts(rows.map((row) => row.summary.workflow?.workflowSelectionOptionalToolMode ?? "none")),
|
|
301
|
+
workflowSelectionExcludedOptionalToolCounts: buildStringCounts(rows.flatMap((row) => row.summary.workflow?.workflowSelectionExcludedOptionalTools ?? [])),
|
|
302
|
+
workflowSelectionActiveProfileCounts: buildStringCounts(rows.flatMap((row) => row.summary.workflow?.workflowSelectionActiveProfileIds ?? [])),
|
|
303
|
+
workflowSelectionProfileToolCounts: buildStringCounts(rows.flatMap((row) => row.summary.workflow?.workflowSelectionProfileTools ?? [])),
|
|
304
|
+
activePlanningStepProfileCounts: buildStringCounts(rows.flatMap((row) => row.summary.workflow?.plan?.activePlanningStepProfileIds ?? [])),
|
|
305
|
+
profileStepCounts: buildStringCounts(rows.flatMap((row) => row.summary.workflow?.plan?.profileStepIds ?? [])),
|
|
306
|
+
outcomeKindCounts: buildStringCounts(rows.flatMap((row) => row.summary.outcomes.map((outcome) => outcome.kind))),
|
|
307
|
+
handledOutcomeKindCounts: buildStringCounts(rows.flatMap((row) => row.summary.outcomes.filter((outcome) => outcome.handled).map((outcome) => outcome.kind))),
|
|
308
|
+
clarificationComparisonMatchedCounts: buildStringCounts(rows.flatMap((row) => row.summary.clarificationComparisons
|
|
309
|
+
.filter((comparison) => comparison.matched === true)
|
|
310
|
+
.map(buildClarificationComparisonMatchedKey))),
|
|
311
|
+
clarificationComparisonMissingRuleCounts: buildStringCounts(rows.flatMap((row) => row.summary.clarificationComparisons.flatMap((comparison) => comparison.missingExpectedRuleIds))),
|
|
312
|
+
clarificationComparisonUnexpectedRuleCounts: buildStringCounts(rows.flatMap((row) => row.summary.clarificationComparisons
|
|
313
|
+
.map((comparison) => comparison.unexpectedObservedRuleId)
|
|
314
|
+
.filter((ruleId) => ruleId !== null))),
|
|
315
|
+
directRoutePolicyEnabledCounts: buildStringCounts(rows.flatMap((row) => row.summary.directRoutePolicies
|
|
316
|
+
.filter((policy) => policy.enabled === true)
|
|
317
|
+
.map((policy) => policy.route ?? "unknown"))),
|
|
318
|
+
directRoutePolicyDisabledReasonCounts: buildStringCounts(rows.flatMap((row) => row.summary.directRoutePolicies
|
|
319
|
+
.filter((policy) => policy.enabled === false)
|
|
320
|
+
.map(buildDirectRoutePolicyDisabledReasonKey))),
|
|
321
|
+
directRouteGapReasonCounts: buildStringCounts(rows.flatMap((row) => row.summary.directRouteGaps.map(buildDirectRouteGapReasonKey))),
|
|
322
|
+
directRouteObservedStrategyCounts: buildStringCounts(rows.flatMap((row) => row.directRouteStrategies.map(buildDirectRouteStrategyKey))),
|
|
323
|
+
directRouteUndeclaredStrategyCounts: buildStringCounts(rows.flatMap((row) => row.directRouteStrategies
|
|
324
|
+
.filter((strategy) => strategy.declared === false)
|
|
325
|
+
.map(buildDirectRouteStrategyKey))),
|
|
326
|
+
directRouteDoneCounts: buildStringCounts(rows.flatMap((row) => row.summary.directRoutes
|
|
327
|
+
.filter((route) => route.outcome === "done")
|
|
328
|
+
.map((route) => route.route))),
|
|
329
|
+
directRouteRecoveredCounts: buildStringCounts(rows.flatMap((row) => row.summary.directRoutes
|
|
330
|
+
.filter((route) => route.outcome === "recovered")
|
|
331
|
+
.map((route) => route.route))),
|
|
332
|
+
directRouteErrorCounts: buildStringCounts(rows.flatMap((row) => row.summary.directRoutes
|
|
333
|
+
.filter((route) => route.outcome === "error")
|
|
334
|
+
.map((route) => route.route))),
|
|
335
|
+
directRouteSkipReasonCounts: buildStringCounts(rows.flatMap((row) => row.summary.directRoutes
|
|
336
|
+
.filter((route) => route.outcome === "skip")
|
|
337
|
+
.map(buildDirectRouteSkipReasonKey)
|
|
338
|
+
.filter((key) => key !== null))),
|
|
339
|
+
memoryChangedSlotCounts: buildStringCounts(rows.flatMap((row) => row.summary.memoryPatches.flatMap((patch) => patch.changedSlots))),
|
|
340
|
+
memoryChangedProfileFieldCounts: buildStringCounts(rows.flatMap((row) => row.summary.memoryPatches.flatMap((patch) => patch.changedProfileFields))),
|
|
341
|
+
memorySkippedPatchReasonCounts: buildStringCounts(rows.flatMap((row) => row.summary.memoryPatches.flatMap((patch) => patch.skippedPatches.map(buildMemorySkippedPatchReasonKey)))),
|
|
342
|
+
memoryPatchEquivalenceCounts: buildStringCounts(rows.flatMap((row) => row.summary.memoryPatches.map(buildMemoryPatchEquivalenceKey))),
|
|
343
|
+
memoryPatchDerivationCounts: buildStringCounts(rows.flatMap((row) => row.summary.memoryPatches.map(buildMemoryPatchDerivationKey))),
|
|
344
|
+
memoryPatchMismatchedProfileFieldCounts: buildStringCounts(rows.flatMap((row) => row.summary.memoryPatches.flatMap((patch) => patch.profilePatchMismatchedFields))),
|
|
345
|
+
memoryPatchWriteSourceCounts: buildStringCounts(rows.flatMap((row) => row.summary.memoryPatches.map(buildMemoryPatchWriteSourceKey))),
|
|
346
|
+
memoryPatchWriteReasonCounts: buildStringCounts(rows.flatMap((row) => row.summary.memoryPatches.map(buildMemoryPatchWriteReasonKey))),
|
|
347
|
+
memoryIntentWritePolicyIntentCounts: buildStringCounts(rows.flatMap((row) => row.summary.memoryPatches.flatMap((patch) => patch.intentWritePolicy?.intentId ? [patch.intentWritePolicy.intentId] : []))),
|
|
348
|
+
memoryIntentWritePolicyEquivalenceCounts: buildStringCounts(rows.flatMap((row) => row.summary.memoryPatches.flatMap((patch) => patch.intentWritePolicy ? [buildMemoryPatchEquivalenceKey(patch.intentWritePolicy)] : []))),
|
|
349
|
+
memoryIntentWritePolicySkippedPatchReasonCounts: buildStringCounts(rows.flatMap((row) => row.summary.memoryPatches.flatMap((patch) => patch.intentWritePolicy?.skippedPatches.map(buildMemorySkippedPatchReasonKey) ?? []))),
|
|
350
|
+
memoryIntentWritePolicyMismatchedProfileFieldCounts: buildStringCounts(rows.flatMap((row) => row.summary.memoryPatches.flatMap((patch) => patch.intentWritePolicy?.profilePatchMismatchedFields ?? []))),
|
|
351
|
+
threadProfileWriteChangedFieldCounts: buildStringCounts(rows.flatMap((row) => row.summary.threadProfileWrites.flatMap((write) => write.changedFields))),
|
|
352
|
+
threadProfileWriteSourceCounts: buildStringCounts(rows.flatMap((row) => row.summary.threadProfileWrites.map(buildThreadProfileWriteSourceKey))),
|
|
353
|
+
threadProfileWriteReasonCounts: buildStringCounts(rows.flatMap((row) => row.summary.threadProfileWrites.map(buildThreadProfileWriteReasonKey))),
|
|
354
|
+
toolRuntimeBindingStatusCounts: buildStringCounts(rows.flatMap((row) => row.summary.toolRuntimeBindings.map((binding) => binding.status ?? "unknown"))),
|
|
355
|
+
toolRuntimeBindingAdapterCounts: buildStringCounts(rows.flatMap((row) => row.summary.toolRuntimeBindings.map((binding) => binding.runtimeAdapterId ?? "unknown"))),
|
|
356
|
+
toolRuntimeBindingPolicyMismatchCounts: buildStringCounts(rows.flatMap((row) => row.summary.toolRuntimeBindings.flatMap((binding) => binding.policyMismatchFields.map((field) => `${binding.toolName ?? "unknown"}:${field}`)))),
|
|
357
|
+
effectiveToolSelectionModeCounts: buildStringCounts(rows.flatMap((row) => row.summary.toolSelections.map((selection) => selection.mode ?? "unknown"))),
|
|
358
|
+
effectiveToolSelectionReasonCounts: buildStringCounts(rows.flatMap((row) => row.summary.toolSelections.map((selection) => selection.reason ?? "unknown"))),
|
|
359
|
+
effectiveToolSelectionToolCounts: buildStringCounts(rows.flatMap((row) => row.summary.toolSelections.flatMap((selection) => selection.toolNames))),
|
|
360
|
+
effectiveToolSelectionSegments: buildEffectiveToolSelectionSegments(rows),
|
|
361
|
+
toolCallCounts: buildStringCounts(rows.flatMap((row) => row.summary.toolCallNames)),
|
|
362
|
+
tuningSignalCounts: buildTuningSignalCounts(rows.flatMap((row) => row.tuningSignals)),
|
|
363
|
+
suggestions: buildReportSuggestions(rows),
|
|
364
|
+
rows,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
export function buildAssistantWorkflowSerializedRunReportFromTraceReportRow(row, options = {}) {
|
|
368
|
+
const signals = buildTraceDerivedRunEvaluationSignals(row);
|
|
369
|
+
const warningCount = signals.filter((signal) => signal.severity === "warning").length;
|
|
370
|
+
const status = buildTraceDerivedRunReportStatus(row, warningCount);
|
|
371
|
+
const intent = row.summary.classifier?.intent ?? row.summary.workflow?.intent ?? null;
|
|
372
|
+
const handledOutcome = row.summary.handledOutcome;
|
|
373
|
+
const outcomeKind = handledOutcome?.kind ?? "trace_summary";
|
|
374
|
+
const outcomeHandled = handledOutcome?.handled ?? status !== "not_handled";
|
|
375
|
+
const outcomeReason = handledOutcome?.reason ?? buildTraceDerivedOutcomeReason(row, status);
|
|
376
|
+
const directRouteDoneRoutes = row.summary.directRoutes
|
|
377
|
+
.filter((route) => route.outcome === "done")
|
|
378
|
+
.map((route) => route.route);
|
|
379
|
+
const directRouteRecoveredRoutes = row.summary.directRoutes
|
|
380
|
+
.filter((route) => route.outcome === "recovered")
|
|
381
|
+
.map((route) => route.route);
|
|
382
|
+
const directRouteErrorRoutes = row.summary.directRoutes
|
|
383
|
+
.filter((route) => route.outcome === "error")
|
|
384
|
+
.map((route) => route.route);
|
|
385
|
+
const directRouteSkipReasons = row.summary.directRoutes
|
|
386
|
+
.filter((route) => route.outcome === "skip")
|
|
387
|
+
.map(buildDirectRouteSkipReasonKey)
|
|
388
|
+
.filter((key) => key !== null);
|
|
389
|
+
const directRoutePolicyEnabledRoutes = row.summary.directRoutePolicies
|
|
390
|
+
.filter((policy) => policy.enabled === true)
|
|
391
|
+
.map((policy) => policy.route ?? "unknown");
|
|
392
|
+
const directRoutePolicyDisabledReasons = row.summary.directRoutePolicies
|
|
393
|
+
.filter((policy) => policy.enabled === false)
|
|
394
|
+
.map(buildDirectRoutePolicyDisabledReasonKey);
|
|
395
|
+
const directRouteGapReasons = row.summary.directRouteGaps.map(buildDirectRouteGapReasonKey);
|
|
396
|
+
const directRouteObservedStrategies = row.directRouteStrategies.map(buildDirectRouteStrategyKey);
|
|
397
|
+
const directRouteUndeclaredStrategies = row.directRouteStrategies
|
|
398
|
+
.filter((strategy) => strategy.declared === false)
|
|
399
|
+
.map(buildDirectRouteStrategyKey);
|
|
400
|
+
const memoryChangedSlots = uniqueStrings(row.summary.memoryPatches.flatMap((patch) => patch.changedSlots));
|
|
401
|
+
const memoryChangedProfileFields = uniqueStrings(row.summary.memoryPatches.flatMap((patch) => patch.changedProfileFields));
|
|
402
|
+
const memorySkippedPatchReasons = row.summary.memoryPatches.flatMap((patch) => patch.skippedPatches.map(buildMemorySkippedPatchReasonKey));
|
|
403
|
+
const memoryPatchEquivalence = uniqueStrings(row.summary.memoryPatches.map(buildMemoryPatchEquivalenceKey));
|
|
404
|
+
const memoryPatchDerivations = uniqueStrings(row.summary.memoryPatches.map(buildMemoryPatchDerivationKey));
|
|
405
|
+
const memoryPatchMismatchedProfileFields = uniqueStrings(row.summary.memoryPatches.flatMap((patch) => patch.profilePatchMismatchedFields));
|
|
406
|
+
const memoryPatchWriteSources = uniqueStrings(row.summary.memoryPatches.map(buildMemoryPatchWriteSourceKey));
|
|
407
|
+
const memoryPatchWriteReasons = uniqueStrings(row.summary.memoryPatches.map(buildMemoryPatchWriteReasonKey));
|
|
408
|
+
const threadProfileWriteSources = uniqueStrings(row.summary.threadProfileWrites.map(buildThreadProfileWriteSourceKey));
|
|
409
|
+
const threadProfileWriteReasons = uniqueStrings(row.summary.threadProfileWrites.map(buildThreadProfileWriteReasonKey));
|
|
410
|
+
const threadProfileWriteChangedFields = uniqueStrings(row.summary.threadProfileWrites.flatMap((write) => write.changedFields));
|
|
411
|
+
const clarificationMatchedRules = row.summary.clarificationComparisons
|
|
412
|
+
.filter((comparison) => comparison.matched === true)
|
|
413
|
+
.map(buildClarificationComparisonMatchedKey);
|
|
414
|
+
const clarificationMissingRules = row.summary.clarificationComparisons.flatMap((comparison) => comparison.missingExpectedRuleIds);
|
|
415
|
+
const clarificationUnexpectedRules = row.summary.clarificationComparisons
|
|
416
|
+
.map((comparison) => comparison.unexpectedObservedRuleId)
|
|
417
|
+
.filter((ruleId) => ruleId !== null);
|
|
418
|
+
const toolResultStatus = row.summary.errorStages.length > 0
|
|
419
|
+
? "failed"
|
|
420
|
+
: row.summary.toolCalls.length > 0
|
|
421
|
+
? "complete"
|
|
422
|
+
: null;
|
|
423
|
+
return {
|
|
424
|
+
schemaVersion: ASSISTANT_WORKFLOW_RUN_REPORT_JSON_SCHEMA_VERSION,
|
|
425
|
+
workflowId: normalizeRunReportWorkflowId(options.workflowId),
|
|
426
|
+
workflowVersion: normalizeRunReportWorkflowVersion(options.workflowVersion),
|
|
427
|
+
status,
|
|
428
|
+
run: {
|
|
429
|
+
source: "trace_report",
|
|
430
|
+
requestId: row.requestId,
|
|
431
|
+
debugId: row.debugId,
|
|
432
|
+
threadId: row.threadId,
|
|
433
|
+
createdAt: row.createdAt,
|
|
434
|
+
status: status === "not_handled" ? "blocked" : status === "failed" ? "partial" : "ready",
|
|
435
|
+
decision: {
|
|
436
|
+
intent,
|
|
437
|
+
presentation: row.summary.classifier?.presentation ?? row.summary.workflow?.effectivePresentation ?? null,
|
|
438
|
+
confidence: row.summary.classifier?.confidence ?? null,
|
|
439
|
+
toolsToExpose: row.summary.classifier?.toolsToExpose ?? [],
|
|
440
|
+
plan: row.summary.workflow?.plan ?? null,
|
|
441
|
+
authority: row.summary.workflow?.authority ?? null,
|
|
442
|
+
},
|
|
443
|
+
toolInvocationQueue: {
|
|
444
|
+
invocationCount: row.summary.toolCalls.length,
|
|
445
|
+
toolNames: row.summary.toolCallNames,
|
|
446
|
+
},
|
|
447
|
+
directRoutes: row.summary.directRoutes.map((route) => ({
|
|
448
|
+
route: route.route,
|
|
449
|
+
outcome: route.outcome,
|
|
450
|
+
reason: route.reason ?? null,
|
|
451
|
+
strategy: route.strategy ?? null,
|
|
452
|
+
topic: route.topic ?? null,
|
|
453
|
+
tool: route.tool ?? null,
|
|
454
|
+
mode: route.mode ?? null,
|
|
455
|
+
handled: route.handled ?? null,
|
|
456
|
+
contentChars: route.contentChars ?? null,
|
|
457
|
+
tookMs: route.tookMs ?? null,
|
|
458
|
+
})),
|
|
459
|
+
directRoutePolicies: row.summary.directRoutePolicies.map((policy) => ({
|
|
460
|
+
intent: policy.intent ?? null,
|
|
461
|
+
route: policy.route ?? null,
|
|
462
|
+
enabled: policy.enabled ?? null,
|
|
463
|
+
reason: policy.reason ?? null,
|
|
464
|
+
policyRouteIds: policy.policyRouteIds,
|
|
465
|
+
})),
|
|
466
|
+
directRouteGaps: row.summary.directRouteGaps.map((gap) => ({
|
|
467
|
+
handlerId: gap.handlerId ?? null,
|
|
468
|
+
layer: gap.layer ?? null,
|
|
469
|
+
route: gap.route ?? null,
|
|
470
|
+
reason: gap.reason ?? null,
|
|
471
|
+
})),
|
|
472
|
+
directRouteStrategies: row.directRouteStrategies.map((strategy) => ({
|
|
473
|
+
route: strategy.route,
|
|
474
|
+
strategy: strategy.strategy,
|
|
475
|
+
definitionRouteId: strategy.definitionRouteId,
|
|
476
|
+
declared: strategy.declared,
|
|
477
|
+
status: strategy.status,
|
|
478
|
+
})),
|
|
479
|
+
memoryPatches: row.summary.memoryPatches.map((patch) => ({
|
|
480
|
+
changedSlots: patch.changedSlots,
|
|
481
|
+
changedProfileFields: patch.changedProfileFields,
|
|
482
|
+
appliedPatchCount: patch.appliedPatchCount,
|
|
483
|
+
skippedPatchCount: patch.skippedPatchCount,
|
|
484
|
+
appliedPatches: patch.appliedPatches.map((appliedPatch) => ({ ...appliedPatch })),
|
|
485
|
+
skippedPatches: patch.skippedPatches.map((skippedPatch) => ({ ...skippedPatch })),
|
|
486
|
+
profilePatchEquivalent: patch.profilePatchEquivalent,
|
|
487
|
+
profilePatchComparedFields: patch.profilePatchComparedFields,
|
|
488
|
+
profilePatchMismatchedFields: patch.profilePatchMismatchedFields,
|
|
489
|
+
patchDerivation: patch.patchDerivation,
|
|
490
|
+
profileWriteMode: patch.profileWriteMode,
|
|
491
|
+
profileWriteSource: patch.profileWriteSource,
|
|
492
|
+
profileWriteReason: patch.profileWriteReason,
|
|
493
|
+
intentWritePolicy: patch.intentWritePolicy ? {
|
|
494
|
+
intentId: patch.intentWritePolicy.intentId,
|
|
495
|
+
writeSlots: patch.intentWritePolicy.writeSlots,
|
|
496
|
+
missingMemorySlots: patch.intentWritePolicy.missingMemorySlots,
|
|
497
|
+
changedSlots: patch.intentWritePolicy.changedSlots,
|
|
498
|
+
appliedPatchCount: patch.intentWritePolicy.appliedPatchCount,
|
|
499
|
+
skippedPatchCount: patch.intentWritePolicy.skippedPatchCount,
|
|
500
|
+
skippedPatches: patch.intentWritePolicy.skippedPatches.map((skippedPatch) => ({ ...skippedPatch })),
|
|
501
|
+
profilePatchEquivalent: patch.intentWritePolicy.profilePatchEquivalent,
|
|
502
|
+
profilePatchComparedFields: patch.intentWritePolicy.profilePatchComparedFields,
|
|
503
|
+
profilePatchMismatchedFields: patch.intentWritePolicy.profilePatchMismatchedFields,
|
|
504
|
+
} : null,
|
|
505
|
+
})),
|
|
506
|
+
threadProfileWrites: row.summary.threadProfileWrites.map((write) => ({
|
|
507
|
+
changed: write.changed,
|
|
508
|
+
changedFields: write.changedFields,
|
|
509
|
+
writeMode: write.writeMode,
|
|
510
|
+
writeSource: write.writeSource,
|
|
511
|
+
writeReason: write.writeReason,
|
|
512
|
+
})),
|
|
513
|
+
outcomes: row.summary.outcomes.map((outcome) => ({
|
|
514
|
+
kind: outcome.kind,
|
|
515
|
+
handled: outcome.handled,
|
|
516
|
+
contentChars: outcome.contentChars ?? null,
|
|
517
|
+
reason: outcome.reason ?? null,
|
|
518
|
+
delivery: outcome.delivery ?? null,
|
|
519
|
+
artifactKind: outcome.artifactKind ?? null,
|
|
520
|
+
route: outcome.route ?? null,
|
|
521
|
+
source: outcome.source ?? null,
|
|
522
|
+
layer: outcome.layer ?? null,
|
|
523
|
+
skipEvent: outcome.skipEvent ?? null,
|
|
524
|
+
mode: outcome.mode ?? null,
|
|
525
|
+
submode: outcome.submode ?? null,
|
|
526
|
+
tool: outcome.tool ?? null,
|
|
527
|
+
ruleId: outcome.ruleId ?? null,
|
|
528
|
+
streamed: outcome.streamed ?? null,
|
|
529
|
+
})),
|
|
530
|
+
clarificationComparisons: row.summary.clarificationComparisons.map((comparison) => ({
|
|
531
|
+
intent: comparison.intent ?? null,
|
|
532
|
+
expectedRuleIds: comparison.expectedRuleIds,
|
|
533
|
+
expectedBlockingRuleIds: comparison.expectedBlockingRuleIds,
|
|
534
|
+
expectedSuggestedRuleIds: comparison.expectedSuggestedRuleIds,
|
|
535
|
+
produced: comparison.produced ?? null,
|
|
536
|
+
observedRuleId: comparison.observedRuleId ?? null,
|
|
537
|
+
equivalentRuleIds: comparison.equivalentRuleIds,
|
|
538
|
+
matched: comparison.matched ?? null,
|
|
539
|
+
missingExpectedRuleIds: comparison.missingExpectedRuleIds,
|
|
540
|
+
unexpectedObservedRuleId: comparison.unexpectedObservedRuleId ?? null,
|
|
541
|
+
source: comparison.source ?? null,
|
|
542
|
+
layer: comparison.layer ?? null,
|
|
543
|
+
reason: comparison.reason ?? null,
|
|
544
|
+
})),
|
|
545
|
+
},
|
|
546
|
+
toolExecution: toolResultStatus === null
|
|
547
|
+
? null
|
|
548
|
+
: {
|
|
549
|
+
status: toolResultStatus,
|
|
550
|
+
totalInvocations: row.summary.toolCalls.length,
|
|
551
|
+
successCount: row.summary.toolCalls.filter((call) => call.success).length,
|
|
552
|
+
errorCount: row.summary.toolCalls.filter((call) => !call.success).length,
|
|
553
|
+
},
|
|
554
|
+
outcome: {
|
|
555
|
+
kind: outcomeKind,
|
|
556
|
+
handled: outcomeHandled,
|
|
557
|
+
reason: outcomeReason,
|
|
558
|
+
delivery: handledOutcome?.delivery ?? null,
|
|
559
|
+
artifactKind: handledOutcome?.artifactKind ?? null,
|
|
560
|
+
route: handledOutcome?.route ?? null,
|
|
561
|
+
contentChars: handledOutcome?.contentChars ?? null,
|
|
562
|
+
source: handledOutcome?.source ?? "trace_summary",
|
|
563
|
+
layer: handledOutcome?.layer ?? null,
|
|
564
|
+
mode: handledOutcome?.mode ?? null,
|
|
565
|
+
submode: handledOutcome?.submode ?? null,
|
|
566
|
+
tool: handledOutcome?.tool ?? null,
|
|
567
|
+
ruleId: handledOutcome?.ruleId ?? null,
|
|
568
|
+
streamed: handledOutcome?.streamed ?? null,
|
|
569
|
+
qualityFlagged: row.summary.quality.flagged,
|
|
570
|
+
feedback: row.feedback,
|
|
571
|
+
},
|
|
572
|
+
evaluation: {
|
|
573
|
+
workflowId: normalizeRunReportWorkflowId(options.workflowId),
|
|
574
|
+
workflowVersion: normalizeRunReportWorkflowVersion(options.workflowVersion),
|
|
575
|
+
status,
|
|
576
|
+
intent,
|
|
577
|
+
outcomeKind,
|
|
578
|
+
handled: outcomeHandled,
|
|
579
|
+
toolResultStatus,
|
|
580
|
+
readyToolCount: row.summary.workflow?.workflowSelectionCandidateTools.length ?? 0,
|
|
581
|
+
invokedToolCount: row.summary.toolCalls.length,
|
|
582
|
+
activePlanningStepProfileIds: row.summary.workflow?.plan?.activePlanningStepProfileIds ?? [],
|
|
583
|
+
profileStepIds: row.summary.workflow?.plan?.profileStepIds ?? [],
|
|
584
|
+
directRouteDoneRoutes,
|
|
585
|
+
directRouteRecoveredRoutes,
|
|
586
|
+
directRouteErrorRoutes,
|
|
587
|
+
directRouteSkipReasons,
|
|
588
|
+
directRoutePolicyEnabledRoutes,
|
|
589
|
+
directRoutePolicyDisabledReasons,
|
|
590
|
+
directRouteGapReasons,
|
|
591
|
+
directRouteObservedStrategies,
|
|
592
|
+
directRouteUndeclaredStrategies,
|
|
593
|
+
memoryChangedSlots,
|
|
594
|
+
memoryChangedProfileFields,
|
|
595
|
+
memorySkippedPatchReasons,
|
|
596
|
+
memoryPatchEquivalence,
|
|
597
|
+
memoryPatchDerivations,
|
|
598
|
+
memoryPatchMismatchedProfileFields,
|
|
599
|
+
memoryPatchWriteSources,
|
|
600
|
+
memoryPatchWriteReasons,
|
|
601
|
+
threadProfileWriteSources,
|
|
602
|
+
threadProfileWriteReasons,
|
|
603
|
+
threadProfileWriteChangedFields,
|
|
604
|
+
clarificationMatchedRules,
|
|
605
|
+
clarificationMissingRules,
|
|
606
|
+
clarificationUnexpectedRules,
|
|
607
|
+
signalCount: signals.length,
|
|
608
|
+
warningCount,
|
|
609
|
+
signals,
|
|
610
|
+
},
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
export function buildAssistantWorkflowSerializedRunReportsFromTraceReport(report, options = {}) {
|
|
614
|
+
return report.rows.map((row) => buildAssistantWorkflowSerializedRunReportFromTraceReportRow(row, options));
|
|
615
|
+
}
|
|
616
|
+
function isInterestingReportRow(row) {
|
|
617
|
+
return row.feedback === "bad" ||
|
|
618
|
+
row.summary.quality.flagged ||
|
|
619
|
+
row.summary.errorStages.length > 0 ||
|
|
620
|
+
row.tuningSignals.some((signal) => signal.severity === "warning");
|
|
621
|
+
}
|
|
622
|
+
function buildStringCounts(values) {
|
|
623
|
+
const counts = new Map();
|
|
624
|
+
for (const value of values) {
|
|
625
|
+
if (!value.length)
|
|
626
|
+
continue;
|
|
627
|
+
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
628
|
+
}
|
|
629
|
+
return [...counts.entries()]
|
|
630
|
+
.map(([key, count]) => ({ key, count }))
|
|
631
|
+
.sort((left, right) => {
|
|
632
|
+
if (right.count !== left.count)
|
|
633
|
+
return right.count - left.count;
|
|
634
|
+
return left.key.localeCompare(right.key);
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
function buildEffectiveToolSelectionSegments(rows) {
|
|
638
|
+
const aggregates = new Map();
|
|
639
|
+
for (const row of rows) {
|
|
640
|
+
const workflowId = row.summary.workflow?.workflowId ?? null;
|
|
641
|
+
const workflowVersion = row.summary.workflow?.workflowVersion ?? null;
|
|
642
|
+
const intentId = row.summary.classifier?.intent ?? row.summary.workflow?.intent ?? "unknown";
|
|
643
|
+
const workflowProfileKey = buildAssistantWorkflowToolSelectionProfileKey({
|
|
644
|
+
intentId: row.summary.workflow?.intent ?? intentId,
|
|
645
|
+
profileIds: row.summary.workflow?.workflowSelectionActiveProfileIds ?? [],
|
|
646
|
+
});
|
|
647
|
+
const authoritySource = row.summary.workflow?.authority?.source ?? null;
|
|
648
|
+
const authorityReason = row.summary.workflow?.authority?.reason ?? null;
|
|
649
|
+
for (const selection of row.summary.toolSelections) {
|
|
650
|
+
const toolSet = buildEffectiveToolSelectionToolSet(selection.toolNames);
|
|
651
|
+
const segmentKey = buildEffectiveToolSelectionSegmentKey({
|
|
652
|
+
workflowId,
|
|
653
|
+
workflowVersion,
|
|
654
|
+
workflowProfileKey,
|
|
655
|
+
intentId,
|
|
656
|
+
hop: selection.hop,
|
|
657
|
+
mode: selection.mode,
|
|
658
|
+
selectionReason: selection.reason,
|
|
659
|
+
pruningEnabled: selection.pruningEnabled,
|
|
660
|
+
toolSignature: toolSet.signature,
|
|
661
|
+
schemaCostSource: selection.schemaCostSource,
|
|
662
|
+
authoritySource,
|
|
663
|
+
authorityReason,
|
|
664
|
+
});
|
|
665
|
+
const aggregate = aggregates.get(segmentKey) ?? {
|
|
666
|
+
segmentKey,
|
|
667
|
+
workflowId,
|
|
668
|
+
workflowVersion,
|
|
669
|
+
workflowProfileKey,
|
|
670
|
+
intentId,
|
|
671
|
+
hop: selection.hop,
|
|
672
|
+
mode: selection.mode,
|
|
673
|
+
selectionReason: selection.reason,
|
|
674
|
+
pruningEnabled: selection.pruningEnabled,
|
|
675
|
+
toolSignature: toolSet.signature,
|
|
676
|
+
toolNames: toolSet.toolNames,
|
|
677
|
+
schemaCostSource: selection.schemaCostSource,
|
|
678
|
+
authoritySource,
|
|
679
|
+
authorityReason,
|
|
680
|
+
selectionCount: 0,
|
|
681
|
+
toolCountObservationCount: 0,
|
|
682
|
+
toolCountTotal: 0,
|
|
683
|
+
totalToolCountObservationCount: 0,
|
|
684
|
+
totalToolCountTotal: 0,
|
|
685
|
+
schemaCharsObservationCount: 0,
|
|
686
|
+
schemaCharsTotal: 0,
|
|
687
|
+
savedCharsObservationCount: 0,
|
|
688
|
+
savedCharsTotal: 0,
|
|
689
|
+
};
|
|
690
|
+
aggregate.selectionCount += 1;
|
|
691
|
+
addToolSelectionMetric(aggregate, "toolCount", selection.toolCount);
|
|
692
|
+
addToolSelectionMetric(aggregate, "totalToolCount", selection.totalToolCount);
|
|
693
|
+
addToolSelectionMetric(aggregate, "schemaChars", selection.approxSchemaChars);
|
|
694
|
+
addToolSelectionMetric(aggregate, "savedChars", selection.approxSavedChars);
|
|
695
|
+
aggregates.set(segmentKey, aggregate);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
return [...aggregates.values()]
|
|
699
|
+
.map((aggregate) => ({
|
|
700
|
+
...aggregate,
|
|
701
|
+
averageToolCount: calculateAverage(aggregate.toolCountTotal, aggregate.toolCountObservationCount),
|
|
702
|
+
averageTotalToolCount: calculateAverage(aggregate.totalToolCountTotal, aggregate.totalToolCountObservationCount),
|
|
703
|
+
averageSchemaChars: calculateAverage(aggregate.schemaCharsTotal, aggregate.schemaCharsObservationCount),
|
|
704
|
+
averageSavedChars: calculateAverage(aggregate.savedCharsTotal, aggregate.savedCharsObservationCount),
|
|
705
|
+
}))
|
|
706
|
+
.sort((left, right) => {
|
|
707
|
+
if (right.selectionCount !== left.selectionCount)
|
|
708
|
+
return right.selectionCount - left.selectionCount;
|
|
709
|
+
return left.segmentKey.localeCompare(right.segmentKey);
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
function buildEffectiveToolSelectionSegmentKey(input) {
|
|
713
|
+
return [
|
|
714
|
+
`workflow_id:${input.workflowId ?? "unknown"}`,
|
|
715
|
+
`workflow_version:${input.workflowVersion ?? "unknown"}`,
|
|
716
|
+
`workflow_profile:${input.workflowProfileKey ?? "none"}`,
|
|
717
|
+
`intent:${input.intentId}`,
|
|
718
|
+
`hop:${input.hop ?? "unknown"}`,
|
|
719
|
+
`mode:${input.mode ?? "unknown"}`,
|
|
720
|
+
`reason:${input.selectionReason ?? "unknown"}`,
|
|
721
|
+
`pruning:${input.pruningEnabled === null ? "unknown" : String(input.pruningEnabled)}`,
|
|
722
|
+
`tools:${input.toolSignature}`,
|
|
723
|
+
`schema_cost:${input.schemaCostSource ?? "unknown"}`,
|
|
724
|
+
`authority:${input.authoritySource ?? "unknown"}`,
|
|
725
|
+
`authority_reason:${input.authorityReason ?? "unknown"}`,
|
|
726
|
+
].join("|");
|
|
727
|
+
}
|
|
728
|
+
function addToolSelectionMetric(aggregate, metric, value) {
|
|
729
|
+
if (value === null || !Number.isFinite(value))
|
|
730
|
+
return;
|
|
731
|
+
const normalized = Math.max(0, value);
|
|
732
|
+
switch (metric) {
|
|
733
|
+
case "toolCount":
|
|
734
|
+
aggregate.toolCountObservationCount += 1;
|
|
735
|
+
aggregate.toolCountTotal += normalized;
|
|
736
|
+
return;
|
|
737
|
+
case "totalToolCount":
|
|
738
|
+
aggregate.totalToolCountObservationCount += 1;
|
|
739
|
+
aggregate.totalToolCountTotal += normalized;
|
|
740
|
+
return;
|
|
741
|
+
case "schemaChars":
|
|
742
|
+
aggregate.schemaCharsObservationCount += 1;
|
|
743
|
+
aggregate.schemaCharsTotal += normalized;
|
|
744
|
+
return;
|
|
745
|
+
case "savedChars":
|
|
746
|
+
aggregate.savedCharsObservationCount += 1;
|
|
747
|
+
aggregate.savedCharsTotal += normalized;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
function calculateAverage(total, count) {
|
|
751
|
+
return count > 0 ? total / count : null;
|
|
752
|
+
}
|
|
753
|
+
const EMPTY_TOOL_SELECTION_METRIC_EVIDENCE_COUNTERS = {
|
|
754
|
+
qualityFlaggedSelectionCount: 0,
|
|
755
|
+
errorSelectionCount: 0,
|
|
756
|
+
badFeedbackSelectionCount: 0,
|
|
757
|
+
unavailableOutcomeSelectionCount: 0,
|
|
758
|
+
};
|
|
759
|
+
function buildToolSelectionMetricEvidenceCounters(rows) {
|
|
760
|
+
const counters = new Map();
|
|
761
|
+
for (const row of rows) {
|
|
762
|
+
const workflowId = row.summary.workflow?.workflowId ?? null;
|
|
763
|
+
const workflowVersion = row.summary.workflow?.workflowVersion ?? null;
|
|
764
|
+
const intentId = row.summary.classifier?.intent ?? row.summary.workflow?.intent ?? "unknown";
|
|
765
|
+
const workflowProfileKey = buildAssistantWorkflowToolSelectionProfileKey({
|
|
766
|
+
intentId: row.summary.workflow?.intent ?? intentId,
|
|
767
|
+
profileIds: row.summary.workflow?.workflowSelectionActiveProfileIds ?? [],
|
|
768
|
+
});
|
|
769
|
+
const authoritySource = row.summary.workflow?.authority?.source ?? null;
|
|
770
|
+
const authorityReason = row.summary.workflow?.authority?.reason ?? null;
|
|
771
|
+
for (const selection of row.summary.toolSelections) {
|
|
772
|
+
const toolSet = buildEffectiveToolSelectionToolSet(selection.toolNames);
|
|
773
|
+
const segmentKey = buildEffectiveToolSelectionSegmentKey({
|
|
774
|
+
workflowId,
|
|
775
|
+
workflowVersion,
|
|
776
|
+
workflowProfileKey,
|
|
777
|
+
intentId,
|
|
778
|
+
hop: selection.hop,
|
|
779
|
+
mode: selection.mode,
|
|
780
|
+
selectionReason: selection.reason,
|
|
781
|
+
pruningEnabled: selection.pruningEnabled,
|
|
782
|
+
toolSignature: toolSet.signature,
|
|
783
|
+
schemaCostSource: selection.schemaCostSource,
|
|
784
|
+
authoritySource,
|
|
785
|
+
authorityReason,
|
|
786
|
+
});
|
|
787
|
+
const current = counters.get(segmentKey) ?? {
|
|
788
|
+
qualityFlaggedSelectionCount: 0,
|
|
789
|
+
errorSelectionCount: 0,
|
|
790
|
+
badFeedbackSelectionCount: 0,
|
|
791
|
+
unavailableOutcomeSelectionCount: 0,
|
|
792
|
+
};
|
|
793
|
+
if (row.summary.quality.flagged)
|
|
794
|
+
current.qualityFlaggedSelectionCount += 1;
|
|
795
|
+
if (row.summary.errorStages.length > 0)
|
|
796
|
+
current.errorSelectionCount += 1;
|
|
797
|
+
if (row.feedback === "bad")
|
|
798
|
+
current.badFeedbackSelectionCount += 1;
|
|
799
|
+
if (row.summary.handledOutcome?.kind === "unavailable") {
|
|
800
|
+
current.unavailableOutcomeSelectionCount += 1;
|
|
801
|
+
}
|
|
802
|
+
counters.set(segmentKey, current);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return counters;
|
|
806
|
+
}
|
|
807
|
+
function buildEffectiveToolSelectionToolSet(toolNames) {
|
|
808
|
+
const normalized = uniqueStrings(toolNames
|
|
809
|
+
.map((toolName) => toolName.trim())
|
|
810
|
+
.filter((toolName) => toolName.length > 0))
|
|
811
|
+
.sort((left, right) => left.localeCompare(right));
|
|
812
|
+
return {
|
|
813
|
+
signature: normalized.length ? normalized.join(",") : "none",
|
|
814
|
+
toolNames: normalized,
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function isToolSelectionMetricSegmentInScope(segment, workflowIds, workflowVersions, workflowProfileKeys, authoritySources, intentIds, segmentKeys) {
|
|
818
|
+
return (!workflowIds.length || (segment.workflowId !== null && workflowIds.includes(segment.workflowId))) &&
|
|
819
|
+
(!workflowVersions.length || (segment.workflowVersion !== null && workflowVersions.includes(segment.workflowVersion))) &&
|
|
820
|
+
(!workflowProfileKeys.length || (segment.workflowProfileKey !== null && workflowProfileKeys.includes(segment.workflowProfileKey))) &&
|
|
821
|
+
(!authoritySources.length || (segment.authoritySource !== null && authoritySources.includes(segment.authoritySource))) &&
|
|
822
|
+
(!intentIds.length || intentIds.includes(segment.intentId)) &&
|
|
823
|
+
(!segmentKeys.length || segmentKeys.includes(segment.segmentKey));
|
|
824
|
+
}
|
|
825
|
+
function selectWorkflowScopedTraceRows(rows, workflowIds, workflowVersions) {
|
|
826
|
+
if (!workflowIds.length && !workflowVersions.length)
|
|
827
|
+
return [...rows];
|
|
828
|
+
return rows.filter((row) => {
|
|
829
|
+
const workflow = row.summary.workflow;
|
|
830
|
+
if (!workflow)
|
|
831
|
+
return false;
|
|
832
|
+
return (!workflowIds.length || (workflow.workflowId !== null && workflowIds.includes(workflow.workflowId))) && (!workflowVersions.length || (workflow.workflowVersion !== null && workflowVersions.includes(workflow.workflowVersion)));
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
function normalizeStringScope(values) {
|
|
836
|
+
const normalized = new Set();
|
|
837
|
+
for (const value of values ?? []) {
|
|
838
|
+
const trimmed = value.trim();
|
|
839
|
+
if (trimmed.length)
|
|
840
|
+
normalized.add(trimmed);
|
|
841
|
+
}
|
|
842
|
+
return [...normalized].sort();
|
|
843
|
+
}
|
|
844
|
+
function normalizeNumberScope(values) {
|
|
845
|
+
const normalized = new Set();
|
|
846
|
+
for (const value of values ?? []) {
|
|
847
|
+
if (Number.isInteger(value) && value > 0)
|
|
848
|
+
normalized.add(value);
|
|
849
|
+
}
|
|
850
|
+
return [...normalized].sort((left, right) => left - right);
|
|
851
|
+
}
|
|
852
|
+
function normalizeOptionalString(value) {
|
|
853
|
+
const trimmed = value?.trim();
|
|
854
|
+
return trimmed?.length ? trimmed : null;
|
|
855
|
+
}
|
|
856
|
+
function findReportCount(counts, key) {
|
|
857
|
+
return counts.find((item) => item.key === key)?.count ?? 0;
|
|
858
|
+
}
|
|
859
|
+
function sumWriteSourceCountsBySource(counts, source) {
|
|
860
|
+
const suffix = `:${source}`;
|
|
861
|
+
return counts.reduce((sum, item) => item.key.endsWith(suffix) ? sum + item.count : sum, 0);
|
|
862
|
+
}
|
|
863
|
+
function normalizePositiveInt(value, fallback) {
|
|
864
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
865
|
+
return fallback;
|
|
866
|
+
const normalized = Math.floor(value);
|
|
867
|
+
return normalized > 0 ? normalized : fallback;
|
|
868
|
+
}
|
|
869
|
+
function normalizeNonNegativeInt(value, fallback) {
|
|
870
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
871
|
+
return fallback;
|
|
872
|
+
const normalized = Math.floor(value);
|
|
873
|
+
return normalized >= 0 ? normalized : fallback;
|
|
874
|
+
}
|
|
875
|
+
function normalizePatchDerivationRequirements(requirements) {
|
|
876
|
+
if (!requirements?.length)
|
|
877
|
+
return [];
|
|
878
|
+
const out = new Map();
|
|
879
|
+
for (const requirement of requirements) {
|
|
880
|
+
const key = requirement.key.trim();
|
|
881
|
+
if (!key.length)
|
|
882
|
+
continue;
|
|
883
|
+
const minCount = normalizePositiveInt(requirement.minCount, 1);
|
|
884
|
+
out.set(key, Math.max(out.get(key) ?? 0, minCount));
|
|
885
|
+
}
|
|
886
|
+
return [...out.entries()]
|
|
887
|
+
.map(([key, count]) => ({ key, count }))
|
|
888
|
+
.sort((left, right) => left.key.localeCompare(right.key));
|
|
889
|
+
}
|
|
890
|
+
function buildTuningSignalCounts(values) {
|
|
891
|
+
const counts = new Map();
|
|
892
|
+
for (const signal of values) {
|
|
893
|
+
const current = counts.get(signal.name);
|
|
894
|
+
if (current) {
|
|
895
|
+
current.count += 1;
|
|
896
|
+
if (signal.severity === "warning")
|
|
897
|
+
current.severity = "warning";
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
counts.set(signal.name, {
|
|
901
|
+
name: signal.name,
|
|
902
|
+
severity: signal.severity,
|
|
903
|
+
count: 1,
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
return [...counts.values()].sort((left, right) => {
|
|
907
|
+
if (right.count !== left.count)
|
|
908
|
+
return right.count - left.count;
|
|
909
|
+
if (left.severity !== right.severity)
|
|
910
|
+
return left.severity === "warning" ? -1 : 1;
|
|
911
|
+
return left.name.localeCompare(right.name);
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
function buildDirectRouteSkipReasonKey(input) {
|
|
915
|
+
if (input.reason === "runtime_policy_disabled" || input.reason === "runtime_disabled")
|
|
916
|
+
return null;
|
|
917
|
+
return input.reason ? `${input.route}:${input.reason}` : `${input.route}:unknown`;
|
|
918
|
+
}
|
|
919
|
+
function buildDirectRoutePolicyDisabledReasonKey(input) {
|
|
920
|
+
return `${input.route ?? "unknown"}:${input.reason ?? "unknown"}`;
|
|
921
|
+
}
|
|
922
|
+
function buildDirectRouteGapReasonKey(input) {
|
|
923
|
+
return `${input.route ?? "unknown"}:${input.reason ?? "unknown"}`;
|
|
924
|
+
}
|
|
925
|
+
function buildMemorySkippedPatchReasonKey(input) {
|
|
926
|
+
return `${input.slotId ?? "unknown"}:${input.operation ?? "unknown"}:${input.reason}`;
|
|
927
|
+
}
|
|
928
|
+
function buildMemoryPatchEquivalenceKey(input) {
|
|
929
|
+
if (input.profilePatchEquivalent === true)
|
|
930
|
+
return "equivalent";
|
|
931
|
+
if (input.profilePatchEquivalent === false)
|
|
932
|
+
return "mismatch";
|
|
933
|
+
return "unknown";
|
|
934
|
+
}
|
|
935
|
+
function buildMemoryPatchDerivationKey(input) {
|
|
936
|
+
return input.patchDerivation ?? "unknown";
|
|
937
|
+
}
|
|
938
|
+
function buildMemoryPatchWriteSourceKey(input) {
|
|
939
|
+
const mode = input.profileWriteMode ?? "unknown";
|
|
940
|
+
const source = input.profileWriteSource ?? "unknown";
|
|
941
|
+
return `${mode}:${source}`;
|
|
942
|
+
}
|
|
943
|
+
function buildMemoryPatchWriteReasonKey(input) {
|
|
944
|
+
return input.profileWriteReason ?? "unknown";
|
|
945
|
+
}
|
|
946
|
+
function buildThreadProfileWriteSourceKey(input) {
|
|
947
|
+
const mode = input.writeMode ?? "unknown";
|
|
948
|
+
const source = input.writeSource ?? "unknown";
|
|
949
|
+
return `${mode}:${source}`;
|
|
950
|
+
}
|
|
951
|
+
function buildThreadProfileWriteReasonKey(input) {
|
|
952
|
+
return input.writeReason ?? "unknown";
|
|
953
|
+
}
|
|
954
|
+
function buildDirectRouteDefinitionLookup(workflow) {
|
|
955
|
+
if (!workflow)
|
|
956
|
+
return null;
|
|
957
|
+
const lookup = new Map();
|
|
958
|
+
for (const directRoute of workflow.directRoutes ?? []) {
|
|
959
|
+
addDirectRouteLookupEntry(lookup, directRoute.id, directRoute);
|
|
960
|
+
if (directRoute.outcomeRoute) {
|
|
961
|
+
addDirectRouteLookupEntry(lookup, directRoute.outcomeRoute, directRoute);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
return lookup;
|
|
965
|
+
}
|
|
966
|
+
function addDirectRouteLookupEntry(lookup, routeId, definition) {
|
|
967
|
+
const normalized = routeId.trim();
|
|
968
|
+
if (!normalized.length || lookup.has(normalized))
|
|
969
|
+
return;
|
|
970
|
+
lookup.set(normalized, definition);
|
|
971
|
+
}
|
|
972
|
+
function buildDirectRouteStrategySummaries(summary, directRouteDefinitionLookup) {
|
|
973
|
+
return summary.directRoutes.flatMap((route) => {
|
|
974
|
+
const strategy = route.strategy?.trim();
|
|
975
|
+
if (!strategy)
|
|
976
|
+
return [];
|
|
977
|
+
if (directRouteDefinitionLookup === null) {
|
|
978
|
+
return [{
|
|
979
|
+
route: route.route,
|
|
980
|
+
strategy,
|
|
981
|
+
definitionRouteId: null,
|
|
982
|
+
declared: null,
|
|
983
|
+
status: "workflow_not_provided",
|
|
984
|
+
}];
|
|
985
|
+
}
|
|
986
|
+
const definition = directRouteDefinitionLookup.get(route.route);
|
|
987
|
+
if (!definition) {
|
|
988
|
+
return [{
|
|
989
|
+
route: route.route,
|
|
990
|
+
strategy,
|
|
991
|
+
definitionRouteId: null,
|
|
992
|
+
declared: false,
|
|
993
|
+
status: "route_not_declared",
|
|
994
|
+
}];
|
|
995
|
+
}
|
|
996
|
+
const declaredStrategyIds = new Set((definition.strategies ?? []).map((item) => item.id));
|
|
997
|
+
const declared = declaredStrategyIds.has(strategy);
|
|
998
|
+
return [{
|
|
999
|
+
route: route.route,
|
|
1000
|
+
strategy,
|
|
1001
|
+
definitionRouteId: definition.id,
|
|
1002
|
+
declared,
|
|
1003
|
+
status: declared ? "declared" : "strategy_not_declared",
|
|
1004
|
+
}];
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
function buildDirectRouteStrategyKey(input) {
|
|
1008
|
+
return `${input.route}:${input.strategy}`;
|
|
1009
|
+
}
|
|
1010
|
+
function uniqueStrings(values) {
|
|
1011
|
+
const out = [];
|
|
1012
|
+
const seen = new Set();
|
|
1013
|
+
for (const value of values) {
|
|
1014
|
+
if (!value.length || seen.has(value))
|
|
1015
|
+
continue;
|
|
1016
|
+
seen.add(value);
|
|
1017
|
+
out.push(value);
|
|
1018
|
+
}
|
|
1019
|
+
return out;
|
|
1020
|
+
}
|
|
1021
|
+
function buildClarificationComparisonMatchedKey(input) {
|
|
1022
|
+
return input.observedRuleId ?? "none";
|
|
1023
|
+
}
|
|
1024
|
+
function isClarificationBlockingRuleSatisfiedByRuntime(row, ruleId) {
|
|
1025
|
+
return row.summary.clarificationComparisons.some((comparison) => comparison.matched === true &&
|
|
1026
|
+
comparison.produced === true &&
|
|
1027
|
+
(comparison.expectedBlockingRuleIds.includes(ruleId) ||
|
|
1028
|
+
comparison.expectedRuleIds.includes(ruleId)));
|
|
1029
|
+
}
|
|
1030
|
+
function buildReportSuggestions(rows) {
|
|
1031
|
+
const builder = new SuggestionBuilder();
|
|
1032
|
+
for (const row of rows) {
|
|
1033
|
+
const intent = row.summary.classifier?.intent ?? row.summary.workflow?.intent ?? null;
|
|
1034
|
+
const workflow = row.summary.workflow;
|
|
1035
|
+
for (const tool of workflow?.missingRequiredToolHints ?? []) {
|
|
1036
|
+
builder.add({
|
|
1037
|
+
kind: "review_missing_required_tool",
|
|
1038
|
+
severity: "warning",
|
|
1039
|
+
intent,
|
|
1040
|
+
tool,
|
|
1041
|
+
signal: null,
|
|
1042
|
+
requestId: row.requestId,
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
if (workflow && hasAssistantWorkflowConstrainedToolSelectionEvidence(workflow)) {
|
|
1046
|
+
for (const tool of getMissingWorkflowSelectionCandidateTools(workflow)) {
|
|
1047
|
+
builder.add({
|
|
1048
|
+
kind: "review_workflow_tool_selection_gap",
|
|
1049
|
+
severity: "info",
|
|
1050
|
+
intent,
|
|
1051
|
+
tool,
|
|
1052
|
+
signal: null,
|
|
1053
|
+
requestId: row.requestId,
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
for (const tool of getAssistantWorkflowUnmodeledToolCalls(workflow, row.summary.toolCallNames)) {
|
|
1057
|
+
builder.add({
|
|
1058
|
+
kind: "review_unmodeled_selected_tool",
|
|
1059
|
+
severity: "info",
|
|
1060
|
+
intent,
|
|
1061
|
+
tool,
|
|
1062
|
+
signal: null,
|
|
1063
|
+
requestId: row.requestId,
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
for (const tool of workflow?.extraClassifierTools ?? []) {
|
|
1068
|
+
builder.add({
|
|
1069
|
+
kind: "review_classifier_extra_tool",
|
|
1070
|
+
severity: "info",
|
|
1071
|
+
intent,
|
|
1072
|
+
tool,
|
|
1073
|
+
signal: null,
|
|
1074
|
+
requestId: row.requestId,
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
for (const ruleId of workflow?.clarification?.blockingRuleIds ?? []) {
|
|
1078
|
+
if (isClarificationBlockingRuleSatisfiedByRuntime(row, ruleId))
|
|
1079
|
+
continue;
|
|
1080
|
+
builder.add({
|
|
1081
|
+
kind: "review_clarification_policy",
|
|
1082
|
+
severity: "warning",
|
|
1083
|
+
intent,
|
|
1084
|
+
tool: null,
|
|
1085
|
+
signal: ruleId,
|
|
1086
|
+
requestId: row.requestId,
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
for (const comparison of row.summary.clarificationComparisons) {
|
|
1090
|
+
for (const ruleId of comparison.missingExpectedRuleIds) {
|
|
1091
|
+
builder.add({
|
|
1092
|
+
kind: "review_clarification_policy",
|
|
1093
|
+
severity: "warning",
|
|
1094
|
+
intent: comparison.intent ?? intent,
|
|
1095
|
+
tool: null,
|
|
1096
|
+
signal: ruleId,
|
|
1097
|
+
requestId: row.requestId,
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
if (comparison.unexpectedObservedRuleId) {
|
|
1101
|
+
builder.add({
|
|
1102
|
+
kind: "review_clarification_policy",
|
|
1103
|
+
severity: "warning",
|
|
1104
|
+
intent: comparison.intent ?? intent,
|
|
1105
|
+
tool: null,
|
|
1106
|
+
signal: comparison.unexpectedObservedRuleId,
|
|
1107
|
+
requestId: row.requestId,
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
for (const patch of row.summary.memoryPatches) {
|
|
1112
|
+
if (patch.profilePatchEquivalent !== false)
|
|
1113
|
+
continue;
|
|
1114
|
+
builder.add({
|
|
1115
|
+
kind: "review_memory_patch_equivalence",
|
|
1116
|
+
severity: "warning",
|
|
1117
|
+
intent,
|
|
1118
|
+
tool: null,
|
|
1119
|
+
signal: patch.profilePatchMismatchedFields.join(", ") || "profile_patch_mismatch",
|
|
1120
|
+
requestId: row.requestId,
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
for (const strategy of row.directRouteStrategies) {
|
|
1124
|
+
if (strategy.declared !== false)
|
|
1125
|
+
continue;
|
|
1126
|
+
builder.add({
|
|
1127
|
+
kind: "review_direct_route_strategy",
|
|
1128
|
+
severity: "info",
|
|
1129
|
+
intent: strategy.definitionRouteId ?? strategy.route,
|
|
1130
|
+
tool: null,
|
|
1131
|
+
signal: buildDirectRouteStrategyKey(strategy),
|
|
1132
|
+
requestId: row.requestId,
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
for (const stage of row.summary.errorStages) {
|
|
1136
|
+
builder.add({
|
|
1137
|
+
kind: "review_trace_error",
|
|
1138
|
+
severity: "warning",
|
|
1139
|
+
intent,
|
|
1140
|
+
tool: null,
|
|
1141
|
+
signal: stage,
|
|
1142
|
+
requestId: row.requestId,
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
for (const signal of row.summary.quality.signalNames) {
|
|
1146
|
+
builder.add({
|
|
1147
|
+
kind: "review_quality_signal",
|
|
1148
|
+
severity: "warning",
|
|
1149
|
+
intent,
|
|
1150
|
+
tool: null,
|
|
1151
|
+
signal,
|
|
1152
|
+
requestId: row.requestId,
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
if (row.feedback === "bad" && row.evalCandidate) {
|
|
1156
|
+
builder.add({
|
|
1157
|
+
kind: "promote_bad_feedback_eval",
|
|
1158
|
+
severity: "warning",
|
|
1159
|
+
intent,
|
|
1160
|
+
tool: null,
|
|
1161
|
+
signal: "bad_feedback",
|
|
1162
|
+
requestId: row.requestId,
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
return builder.toSuggestions();
|
|
1167
|
+
}
|
|
1168
|
+
function buildTraceDerivedRunReportStatus(row, warningCount) {
|
|
1169
|
+
if (row.summary.handledOutcome?.kind === "clarification")
|
|
1170
|
+
return "clarification";
|
|
1171
|
+
if (row.summary.handledOutcome?.kind === "degraded")
|
|
1172
|
+
return "degraded";
|
|
1173
|
+
if (row.summary.errorStages.length > 0)
|
|
1174
|
+
return "failed";
|
|
1175
|
+
if (warningCount > 0 || row.feedback === "bad" || row.summary.quality.flagged)
|
|
1176
|
+
return "degraded";
|
|
1177
|
+
if (row.summary.handledOutcome)
|
|
1178
|
+
return "completed";
|
|
1179
|
+
if (!row.summary.workflow)
|
|
1180
|
+
return "not_handled";
|
|
1181
|
+
return "completed";
|
|
1182
|
+
}
|
|
1183
|
+
function buildTraceDerivedRunEvaluationSignals(row) {
|
|
1184
|
+
const signals = [];
|
|
1185
|
+
const workflow = row.summary.workflow;
|
|
1186
|
+
if (!workflow && !row.summary.handledOutcome) {
|
|
1187
|
+
signals.push({
|
|
1188
|
+
name: "run_not_handled",
|
|
1189
|
+
severity: "warning",
|
|
1190
|
+
detail: "trace did not include a workflow decision",
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
if (row.summary.errorStages.length > 0) {
|
|
1194
|
+
signals.push({
|
|
1195
|
+
name: "run_failed",
|
|
1196
|
+
severity: "warning",
|
|
1197
|
+
detail: row.summary.errorStages.join(", "),
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
if (workflow?.missingRequiredToolHints.length) {
|
|
1201
|
+
signals.push({
|
|
1202
|
+
name: "missing_required_tools",
|
|
1203
|
+
severity: "warning",
|
|
1204
|
+
detail: workflow.missingRequiredToolHints.join(", "),
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
if (workflow?.plan?.missingRequiredToolHints.length) {
|
|
1208
|
+
signals.push({
|
|
1209
|
+
name: "missing_required_tools",
|
|
1210
|
+
severity: "warning",
|
|
1211
|
+
detail: workflow.plan.missingRequiredToolHints.join(", "),
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
if (row.summary.errorStages.some((stage) => stage === ASSISTANT_WORKFLOW_TRACE_STAGE.toolCallError)) {
|
|
1215
|
+
signals.push({
|
|
1216
|
+
name: "tool_execution_failed",
|
|
1217
|
+
severity: "warning",
|
|
1218
|
+
detail: row.summary.errorStages.join(", "),
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
if (row.feedback === "bad" || row.summary.quality.flagged) {
|
|
1222
|
+
signals.push({
|
|
1223
|
+
name: "run_degraded",
|
|
1224
|
+
severity: "warning",
|
|
1225
|
+
detail: row.summary.quality.flagged
|
|
1226
|
+
? row.summary.quality.signalNames.join(", ") || "quality flagged"
|
|
1227
|
+
: "bad feedback",
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
for (const comparison of row.summary.clarificationComparisons) {
|
|
1231
|
+
if (comparison.matched !== false)
|
|
1232
|
+
continue;
|
|
1233
|
+
signals.push({
|
|
1234
|
+
name: "clarification_policy_mismatch",
|
|
1235
|
+
severity: "warning",
|
|
1236
|
+
detail: buildClarificationPolicyMismatchDetail(comparison),
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
return dedupeRunEvaluationSignals(signals);
|
|
1240
|
+
}
|
|
1241
|
+
function buildClarificationPolicyMismatchDetail(input) {
|
|
1242
|
+
if (input.missingExpectedRuleIds.length)
|
|
1243
|
+
return `missing ${input.missingExpectedRuleIds.join(", ")}`;
|
|
1244
|
+
if (input.unexpectedObservedRuleId)
|
|
1245
|
+
return `unexpected ${input.unexpectedObservedRuleId}`;
|
|
1246
|
+
if (input.observedRuleId)
|
|
1247
|
+
return `unmatched ${input.observedRuleId}`;
|
|
1248
|
+
return "clarification policy mismatch";
|
|
1249
|
+
}
|
|
1250
|
+
function dedupeRunEvaluationSignals(signals) {
|
|
1251
|
+
const out = [];
|
|
1252
|
+
const seen = new Set();
|
|
1253
|
+
for (const signal of signals) {
|
|
1254
|
+
const key = `${signal.name}:${signal.detail ?? ""}`;
|
|
1255
|
+
if (seen.has(key))
|
|
1256
|
+
continue;
|
|
1257
|
+
seen.add(key);
|
|
1258
|
+
out.push(signal);
|
|
1259
|
+
}
|
|
1260
|
+
return out;
|
|
1261
|
+
}
|
|
1262
|
+
function buildTraceDerivedOutcomeReason(row, status) {
|
|
1263
|
+
if (status === "completed")
|
|
1264
|
+
return null;
|
|
1265
|
+
if (!row.summary.workflow)
|
|
1266
|
+
return "trace did not include a workflow decision";
|
|
1267
|
+
if (row.summary.errorStages.length > 0)
|
|
1268
|
+
return row.summary.errorStages.join(", ");
|
|
1269
|
+
if (row.summary.quality.flagged)
|
|
1270
|
+
return row.summary.quality.signalNames.join(", ") || "quality flagged";
|
|
1271
|
+
if (row.feedback === "bad")
|
|
1272
|
+
return "bad feedback";
|
|
1273
|
+
return "trace-derived warning";
|
|
1274
|
+
}
|
|
1275
|
+
function normalizeRunReportWorkflowId(value) {
|
|
1276
|
+
const normalized = value?.trim();
|
|
1277
|
+
return normalized?.length ? normalized : "trace-derived-assistant-workflow";
|
|
1278
|
+
}
|
|
1279
|
+
function normalizeRunReportWorkflowVersion(value) {
|
|
1280
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
1281
|
+
return 1;
|
|
1282
|
+
return Math.max(1, Math.floor(value));
|
|
1283
|
+
}
|
|
1284
|
+
class SuggestionBuilder {
|
|
1285
|
+
suggestions = new Map();
|
|
1286
|
+
add(input) {
|
|
1287
|
+
const id = buildSuggestionId(input);
|
|
1288
|
+
const existing = this.suggestions.get(id);
|
|
1289
|
+
if (existing) {
|
|
1290
|
+
existing.count += 1;
|
|
1291
|
+
if (!existing.requestIds.includes(input.requestId) && existing.requestIds.length < 5) {
|
|
1292
|
+
existing.requestIds.push(input.requestId);
|
|
1293
|
+
}
|
|
1294
|
+
if (input.severity === "warning")
|
|
1295
|
+
existing.severity = "warning";
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
this.suggestions.set(id, {
|
|
1299
|
+
kind: input.kind,
|
|
1300
|
+
severity: input.severity,
|
|
1301
|
+
intent: input.intent,
|
|
1302
|
+
tool: input.tool,
|
|
1303
|
+
signal: input.signal,
|
|
1304
|
+
count: 1,
|
|
1305
|
+
requestIds: [input.requestId],
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
toSuggestions() {
|
|
1309
|
+
return [...this.suggestions.entries()]
|
|
1310
|
+
.map(([id, item]) => ({
|
|
1311
|
+
id,
|
|
1312
|
+
kind: item.kind,
|
|
1313
|
+
severity: item.severity,
|
|
1314
|
+
intent: item.intent,
|
|
1315
|
+
tool: item.tool,
|
|
1316
|
+
signal: item.signal,
|
|
1317
|
+
count: item.count,
|
|
1318
|
+
requestIds: item.requestIds,
|
|
1319
|
+
...describeSuggestion(item),
|
|
1320
|
+
}))
|
|
1321
|
+
.sort((left, right) => {
|
|
1322
|
+
if (left.severity !== right.severity)
|
|
1323
|
+
return left.severity === "warning" ? -1 : 1;
|
|
1324
|
+
if (right.count !== left.count)
|
|
1325
|
+
return right.count - left.count;
|
|
1326
|
+
return left.id.localeCompare(right.id);
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
function buildSuggestionId(input) {
|
|
1331
|
+
return [
|
|
1332
|
+
input.kind,
|
|
1333
|
+
input.intent ?? "unknown",
|
|
1334
|
+
input.tool ?? input.signal ?? "all",
|
|
1335
|
+
].map((part) => part.replace(/[^a-zA-Z0-9_.-]+/g, "_").toLowerCase()).join(":");
|
|
1336
|
+
}
|
|
1337
|
+
function describeSuggestion(item) {
|
|
1338
|
+
const intent = item.intent ?? "unknown intent";
|
|
1339
|
+
const tool = item.tool ?? "unknown tool";
|
|
1340
|
+
const signal = item.signal ?? "unknown signal";
|
|
1341
|
+
switch (item.kind) {
|
|
1342
|
+
case "review_missing_required_tool":
|
|
1343
|
+
return {
|
|
1344
|
+
rationale: `Workflow required ${tool} for ${intent}, but the tool was not available in these traces.`,
|
|
1345
|
+
suggestedAction: "Check tool registration/policy or relax the requiredToolHint/planning-step requirement if it is no longer valid.",
|
|
1346
|
+
};
|
|
1347
|
+
case "review_workflow_tool_selection_gap":
|
|
1348
|
+
return {
|
|
1349
|
+
rationale: `Workflow suggested ${tool} for ${intent}, but existing tool pruning did not select it.`,
|
|
1350
|
+
suggestedAction: "Compare pruning rules against workflow hints before migrating this route to workflow-owned selection.",
|
|
1351
|
+
};
|
|
1352
|
+
case "review_unmodeled_selected_tool":
|
|
1353
|
+
return {
|
|
1354
|
+
rationale: `Existing runtime selected ${tool} for ${intent}, but the workflow did not suggest it.`,
|
|
1355
|
+
suggestedAction: "If this is intentional, add a tool hint or conditional planning step; otherwise tighten the runtime pruning path.",
|
|
1356
|
+
};
|
|
1357
|
+
case "review_classifier_extra_tool":
|
|
1358
|
+
return {
|
|
1359
|
+
rationale: `Classifier exposed ${tool} for ${intent} outside the workflow hint set.`,
|
|
1360
|
+
suggestedAction: "Add an explicit workflow hint/condition or update classifier prompt facts so the contract stays consistent.",
|
|
1361
|
+
};
|
|
1362
|
+
case "review_clarification_policy":
|
|
1363
|
+
return {
|
|
1364
|
+
rationale: `Clarification rule ${signal} blocked confident execution for ${intent}.`,
|
|
1365
|
+
suggestedAction: "Check whether the runtime already clarified correctly; if repeated, promote examples into evals before changing the clarification rule.",
|
|
1366
|
+
};
|
|
1367
|
+
case "review_memory_patch_equivalence":
|
|
1368
|
+
return {
|
|
1369
|
+
rationale: `Workflow memory patches did not reproduce legacy profile fields (${signal}) for ${intent}.`,
|
|
1370
|
+
suggestedAction: "Keep memory writes in shadow mode; inspect the slot mapping/coercion before cutting over profile persistence.",
|
|
1371
|
+
};
|
|
1372
|
+
case "review_direct_route_strategy":
|
|
1373
|
+
return {
|
|
1374
|
+
rationale: `Runtime direct-route strategy ${signal} was observed for ${intent}, but it is not declared in the workflow route catalog.`,
|
|
1375
|
+
suggestedAction: "Review whether this branch should be added to route strategy metadata or split into a narrower direct-route definition.",
|
|
1376
|
+
};
|
|
1377
|
+
case "review_trace_error":
|
|
1378
|
+
return {
|
|
1379
|
+
rationale: `Trace stage ${signal} occurred for ${intent}.`,
|
|
1380
|
+
suggestedAction: "Debug the request path first; tune workflow only after the implementation error is understood.",
|
|
1381
|
+
};
|
|
1382
|
+
case "review_quality_signal":
|
|
1383
|
+
return {
|
|
1384
|
+
rationale: `Quality signal ${signal} fired for ${intent}.`,
|
|
1385
|
+
suggestedAction: "Promote representative prompts to eval cases, then adjust intent, tool, artifact, or clarification policy if repeated.",
|
|
1386
|
+
};
|
|
1387
|
+
case "promote_bad_feedback_eval":
|
|
1388
|
+
return {
|
|
1389
|
+
rationale: `Bad user feedback was recorded for ${intent}.`,
|
|
1390
|
+
suggestedAction: "Review the trace and promote the prompt into an eval case before changing workflow definitions.",
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
//# sourceMappingURL=trace-report.js.map
|