Добавить evidence planner для автономного маршрута
This commit is contained in:
parent
0bd631c160
commit
f34ae5d6df
|
|
@ -0,0 +1,160 @@
|
||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.ASSISTANT_EVIDENCE_PLANNER_SCHEMA_VERSION = void 0;
|
||||||
|
exports.buildAssistantEvidencePlanner = buildAssistantEvidencePlanner;
|
||||||
|
exports.ASSISTANT_EVIDENCE_PLANNER_SCHEMA_VERSION = "assistant_evidence_planner_v1";
|
||||||
|
function toNonEmptyString(value) {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const text = String(value).trim();
|
||||||
|
return text.length > 0 ? text : null;
|
||||||
|
}
|
||||||
|
function normalizeReasonCode(value) {
|
||||||
|
const normalized = value
|
||||||
|
.trim()
|
||||||
|
.replace(/[^\p{L}\p{N}_.:-]+/gu, "_")
|
||||||
|
.replace(/^_+|_+$/g, "")
|
||||||
|
.toLowerCase();
|
||||||
|
return normalized.length > 0 ? normalized.slice(0, 120) : null;
|
||||||
|
}
|
||||||
|
function pushReason(target, value) {
|
||||||
|
const normalized = normalizeReasonCode(value);
|
||||||
|
if (normalized && !target.includes(normalized)) {
|
||||||
|
target.push(normalized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function uniqueStrings(values) {
|
||||||
|
const result = [];
|
||||||
|
for (const value of values) {
|
||||||
|
const text = toNonEmptyString(value);
|
||||||
|
if (text && !result.includes(text)) {
|
||||||
|
result.push(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
function providedAxesFromMeaning(meaning) {
|
||||||
|
const result = [];
|
||||||
|
if ((meaning?.explicit_entity_candidates?.length ?? 0) > 0) {
|
||||||
|
result.push("counterparty");
|
||||||
|
result.push("business_entity");
|
||||||
|
}
|
||||||
|
if (toNonEmptyString(meaning?.explicit_organization_scope)) {
|
||||||
|
result.push("organization");
|
||||||
|
}
|
||||||
|
if (toNonEmptyString(meaning?.explicit_date_scope)) {
|
||||||
|
result.push("period");
|
||||||
|
}
|
||||||
|
if (toNonEmptyString(meaning?.asked_aggregation_axis)) {
|
||||||
|
result.push("aggregate_axis");
|
||||||
|
}
|
||||||
|
if (toNonEmptyString(meaning?.metadata_scope_hint)) {
|
||||||
|
result.push("metadata_scope");
|
||||||
|
}
|
||||||
|
return uniqueStrings(result);
|
||||||
|
}
|
||||||
|
function missingAxes(requiredAxes, providedAxes) {
|
||||||
|
return requiredAxes.filter((axis) => !providedAxes.includes(axis));
|
||||||
|
}
|
||||||
|
function coverageExpectationFor(graph) {
|
||||||
|
if (graph?.proof_expectation === "bounded_inference") {
|
||||||
|
return "bounded_inference";
|
||||||
|
}
|
||||||
|
if (graph?.proof_expectation === "clarification_required") {
|
||||||
|
return "clarification";
|
||||||
|
}
|
||||||
|
return "confirmed_coverage";
|
||||||
|
}
|
||||||
|
function answerModeFor(input) {
|
||||||
|
if (input.plannerStatus === "needs_clarification") {
|
||||||
|
return "clarification_required";
|
||||||
|
}
|
||||||
|
if (input.plannerStatus === "blocked") {
|
||||||
|
return "checked_sources_only";
|
||||||
|
}
|
||||||
|
if (input.coverageExpectation === "bounded_inference") {
|
||||||
|
return "bounded_business_inference";
|
||||||
|
}
|
||||||
|
if (input.coverageExpectation === "clarification") {
|
||||||
|
return "clarification_required";
|
||||||
|
}
|
||||||
|
return "confirmed_business_answer";
|
||||||
|
}
|
||||||
|
function requiredUserLayersFor(answerMode) {
|
||||||
|
if (answerMode === "clarification_required") {
|
||||||
|
return ["clarifying_question", "why_needed", "available_calculation"];
|
||||||
|
}
|
||||||
|
if (answerMode === "bounded_business_inference") {
|
||||||
|
return ["business_conclusion", "key_figures", "evidence_boundary", "next_step"];
|
||||||
|
}
|
||||||
|
if (answerMode === "checked_sources_only") {
|
||||||
|
return ["checked_sources_boundary", "unknowns", "next_probe"];
|
||||||
|
}
|
||||||
|
return ["direct_business_answer", "key_figures", "evidence_boundary", "next_step"];
|
||||||
|
}
|
||||||
|
function buildAssistantEvidencePlanner(input) {
|
||||||
|
const graph = input.dataNeedGraph ?? null;
|
||||||
|
const plan = input.discoveryPlan;
|
||||||
|
const turnMeaning = plan.turn_meaning_ref;
|
||||||
|
const requiredAxes = uniqueStrings(plan.required_axes);
|
||||||
|
const providedAxes = providedAxesFromMeaning(turnMeaning);
|
||||||
|
const graphClarificationGaps = uniqueStrings(graph?.clarification_gaps ?? []);
|
||||||
|
const axisGaps = missingAxes(requiredAxes, providedAxes);
|
||||||
|
const clarificationGaps = uniqueStrings([...graphClarificationGaps, ...axisGaps]);
|
||||||
|
const coverageExpectation = coverageExpectationFor(graph);
|
||||||
|
const answerMode = answerModeFor({
|
||||||
|
plannerStatus: input.plannerStatus,
|
||||||
|
coverageExpectation
|
||||||
|
});
|
||||||
|
const reasonCodes = uniqueStrings(plan.reason_codes);
|
||||||
|
pushReason(reasonCodes, "evidence_planner_contract_built");
|
||||||
|
if (graph) {
|
||||||
|
pushReason(reasonCodes, "evidence_planner_consumed_data_need_graph");
|
||||||
|
}
|
||||||
|
if (clarificationGaps.length > 0) {
|
||||||
|
pushReason(reasonCodes, "evidence_planner_has_missing_axes_or_gaps");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
schema_version: exports.ASSISTANT_EVIDENCE_PLANNER_SCHEMA_VERSION,
|
||||||
|
policy_owner: "assistantEvidencePlanner",
|
||||||
|
planner_status: input.plannerStatus,
|
||||||
|
semantic_data_need: toNonEmptyString(input.semanticDataNeed),
|
||||||
|
selected_chain_id: input.selectedChainId,
|
||||||
|
data_need: {
|
||||||
|
business_fact_family: graph?.business_fact_family ?? null,
|
||||||
|
action_family: graph?.action_family ?? null,
|
||||||
|
aggregation_need: graph?.aggregation_need ?? null,
|
||||||
|
comparison_need: graph?.comparison_need ?? null,
|
||||||
|
ranking_need: graph?.ranking_need ?? null,
|
||||||
|
time_scope_need: graph?.time_scope_need ?? null,
|
||||||
|
proof_expectation: graph?.proof_expectation ?? null,
|
||||||
|
subject_candidates: uniqueStrings(graph?.subject_candidates ?? [])
|
||||||
|
},
|
||||||
|
evidence_axes: {
|
||||||
|
required_axes: requiredAxes,
|
||||||
|
provided_axes: providedAxes,
|
||||||
|
missing_axes: axisGaps,
|
||||||
|
clarification_gaps: clarificationGaps
|
||||||
|
},
|
||||||
|
primitive_plan: {
|
||||||
|
selected_chain_id: input.selectedChainId,
|
||||||
|
allowed_primitives: plan.allowed_primitives,
|
||||||
|
rejected_primitives: plan.rejected_primitives,
|
||||||
|
execution_budget: plan.execution_budget
|
||||||
|
},
|
||||||
|
coverage_gate: {
|
||||||
|
requires_evidence_gate: true,
|
||||||
|
expected_coverage: coverageExpectation,
|
||||||
|
answer_permission_if_satisfied: answerMode,
|
||||||
|
answer_may_use_raw_model_claims: false
|
||||||
|
},
|
||||||
|
answer_contract: {
|
||||||
|
answer_mode: answerMode,
|
||||||
|
required_user_layers: requiredUserLayersFor(answerMode),
|
||||||
|
forbidden_overclaim_flags: uniqueStrings(graph?.forbidden_overclaim_flags ?? []),
|
||||||
|
must_keep_internal_mechanics_hidden: true
|
||||||
|
},
|
||||||
|
reason_codes: reasonCodes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -38,6 +38,10 @@ function isRouteCandidateContract(value) {
|
||||||
return (record?.schema_version === "assistant_mcp_route_candidate_v1" &&
|
return (record?.schema_version === "assistant_mcp_route_candidate_v1" &&
|
||||||
record?.policy_owner === "assistantMcpDiscoveryRuntimeBridge");
|
record?.policy_owner === "assistantMcpDiscoveryRuntimeBridge");
|
||||||
}
|
}
|
||||||
|
function isEvidencePlannerContract(value) {
|
||||||
|
const record = toRecordObject(value);
|
||||||
|
return record?.schema_version === "assistant_evidence_planner_v1" && record?.policy_owner === "assistantEvidencePlanner";
|
||||||
|
}
|
||||||
function resolveEntryPoint(input) {
|
function resolveEntryPoint(input) {
|
||||||
if (isMcpDiscoveryEntryPointContract(input.entryPoint)) {
|
if (isMcpDiscoveryEntryPointContract(input.entryPoint)) {
|
||||||
return input.entryPoint;
|
return input.entryPoint;
|
||||||
|
|
@ -51,6 +55,10 @@ function buildAssistantMcpDiscoveryDebugAttachmentFields(input) {
|
||||||
const entryPoint = resolveEntryPoint(input);
|
const entryPoint = resolveEntryPoint(input);
|
||||||
const bridge = toRecordObject(entryPoint?.bridge);
|
const bridge = toRecordObject(entryPoint?.bridge);
|
||||||
const planner = toRecordObject(bridge?.planner);
|
const planner = toRecordObject(bridge?.planner);
|
||||||
|
const evidencePlan = isEvidencePlannerContract(planner?.evidence_plan) ? planner.evidence_plan : null;
|
||||||
|
const evidencePlanAxes = toRecordObject(evidencePlan?.evidence_axes);
|
||||||
|
const evidencePlanCoverageGate = toRecordObject(evidencePlan?.coverage_gate);
|
||||||
|
const evidencePlanAnswerContract = toRecordObject(evidencePlan?.answer_contract);
|
||||||
const chainAlignment = toRecordObject(planner?.catalog_chain_template_alignment);
|
const chainAlignment = toRecordObject(planner?.catalog_chain_template_alignment);
|
||||||
const routeCandidate = isRouteCandidateContract(bridge?.route_candidate) ? bridge.route_candidate : null;
|
const routeCandidate = isRouteCandidateContract(bridge?.route_candidate) ? bridge.route_candidate : null;
|
||||||
const answerDraft = toRecordObject(bridge?.answer_draft);
|
const answerDraft = toRecordObject(bridge?.answer_draft);
|
||||||
|
|
@ -61,6 +69,11 @@ function buildAssistantMcpDiscoveryDebugAttachmentFields(input) {
|
||||||
mcp_discovery_hot_runtime_wired: false,
|
mcp_discovery_hot_runtime_wired: false,
|
||||||
mcp_discovery_bridge_status: toNonEmptyString(bridge?.bridge_status),
|
mcp_discovery_bridge_status: toNonEmptyString(bridge?.bridge_status),
|
||||||
mcp_discovery_selected_chain_id: toNonEmptyString(planner?.selected_chain_id),
|
mcp_discovery_selected_chain_id: toNonEmptyString(planner?.selected_chain_id),
|
||||||
|
mcp_discovery_evidence_plan_v1: evidencePlan,
|
||||||
|
mcp_discovery_evidence_plan_status: toNonEmptyString(evidencePlan?.planner_status),
|
||||||
|
mcp_discovery_evidence_plan_answer_mode: toNonEmptyString(evidencePlanAnswerContract?.answer_mode),
|
||||||
|
mcp_discovery_evidence_plan_missing_axes: toStringArray(evidencePlanAxes?.missing_axes),
|
||||||
|
mcp_discovery_evidence_plan_expected_coverage: toNonEmptyString(evidencePlanCoverageGate?.expected_coverage),
|
||||||
mcp_discovery_catalog_chain_template_matches: toStringArray(planner?.catalog_chain_template_matches),
|
mcp_discovery_catalog_chain_template_matches: toStringArray(planner?.catalog_chain_template_matches),
|
||||||
mcp_discovery_catalog_chain_alignment_status: toNonEmptyString(chainAlignment?.alignment_status),
|
mcp_discovery_catalog_chain_alignment_status: toNonEmptyString(chainAlignment?.alignment_status),
|
||||||
mcp_discovery_catalog_chain_top_match: toNonEmptyString(chainAlignment?.top_chain_template_match),
|
mcp_discovery_catalog_chain_top_match: toNonEmptyString(chainAlignment?.top_chain_template_match),
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ exports.ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION = void 0;
|
||||||
exports.planAssistantMcpDiscovery = planAssistantMcpDiscovery;
|
exports.planAssistantMcpDiscovery = planAssistantMcpDiscovery;
|
||||||
const assistantMcpDiscoveryPolicy_1 = require("./assistantMcpDiscoveryPolicy");
|
const assistantMcpDiscoveryPolicy_1 = require("./assistantMcpDiscoveryPolicy");
|
||||||
const assistantMcpCatalogIndex_1 = require("./assistantMcpCatalogIndex");
|
const assistantMcpCatalogIndex_1 = require("./assistantMcpCatalogIndex");
|
||||||
|
const assistantEvidencePlanner_1 = require("./assistantEvidencePlanner");
|
||||||
exports.ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION = "assistant_mcp_discovery_planner_v1";
|
exports.ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION = "assistant_mcp_discovery_planner_v1";
|
||||||
const CHUNKED_COVERAGE_PROBE_BUDGET = 30;
|
const CHUNKED_COVERAGE_PROBE_BUDGET = 30;
|
||||||
function toNonEmptyString(value) {
|
function toNonEmptyString(value) {
|
||||||
|
|
@ -1139,6 +1140,13 @@ function planAssistantMcpDiscovery(input) {
|
||||||
else {
|
else {
|
||||||
pushReason(reasonCodes, "planner_needs_more_user_or_scope_context");
|
pushReason(reasonCodes, "planner_needs_more_user_or_scope_context");
|
||||||
}
|
}
|
||||||
|
const evidencePlan = (0, assistantEvidencePlanner_1.buildAssistantEvidencePlanner)({
|
||||||
|
selectedChainId: recipe.chainId,
|
||||||
|
plannerStatus,
|
||||||
|
semanticDataNeed,
|
||||||
|
dataNeedGraph,
|
||||||
|
discoveryPlan: plan
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION,
|
schema_version: exports.ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION,
|
||||||
policy_owner: "assistantMcpDiscoveryPlanner",
|
policy_owner: "assistantMcpDiscoveryPlanner",
|
||||||
|
|
@ -1152,6 +1160,7 @@ function planAssistantMcpDiscovery(input) {
|
||||||
catalog_chain_template_alignment: catalogChainTemplateAlignment,
|
catalog_chain_template_alignment: catalogChainTemplateAlignment,
|
||||||
proposed_primitives: recipe.primitives,
|
proposed_primitives: recipe.primitives,
|
||||||
required_axes: recipe.axes,
|
required_axes: recipe.axes,
|
||||||
|
evidence_plan: evidencePlan,
|
||||||
discovery_plan: plan,
|
discovery_plan: plan,
|
||||||
catalog_review: adjustedReview,
|
catalog_review: adjustedReview,
|
||||||
reason_codes: reasonCodes
|
reason_codes: reasonCodes
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,251 @@
|
||||||
|
import type { AssistantMcpDiscoveryDataNeedGraphContract } from "./assistantMcpDiscoveryDataNeedGraph";
|
||||||
|
import type {
|
||||||
|
AssistantMcpDiscoveryPlanContract,
|
||||||
|
AssistantMcpDiscoveryPrimitive,
|
||||||
|
AssistantMcpDiscoveryTurnMeaningRef
|
||||||
|
} from "./assistantMcpDiscoveryPolicy";
|
||||||
|
|
||||||
|
export const ASSISTANT_EVIDENCE_PLANNER_SCHEMA_VERSION = "assistant_evidence_planner_v1" as const;
|
||||||
|
|
||||||
|
export type AssistantEvidencePlannerStatus = "ready_for_execution" | "needs_clarification" | "blocked";
|
||||||
|
export type AssistantEvidenceCoverageExpectation = "confirmed_coverage" | "bounded_inference" | "clarification";
|
||||||
|
export type AssistantEvidenceAnswerMode =
|
||||||
|
| "confirmed_business_answer"
|
||||||
|
| "bounded_business_inference"
|
||||||
|
| "clarification_required"
|
||||||
|
| "checked_sources_only";
|
||||||
|
|
||||||
|
export interface AssistantEvidenceDataNeedContract {
|
||||||
|
business_fact_family: string | null;
|
||||||
|
action_family: string | null;
|
||||||
|
aggregation_need: string | null;
|
||||||
|
comparison_need: string | null;
|
||||||
|
ranking_need: string | null;
|
||||||
|
time_scope_need: string | null;
|
||||||
|
proof_expectation: string | null;
|
||||||
|
subject_candidates: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssistantEvidenceAxesContract {
|
||||||
|
required_axes: string[];
|
||||||
|
provided_axes: string[];
|
||||||
|
missing_axes: string[];
|
||||||
|
clarification_gaps: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssistantEvidencePrimitivePlanContract {
|
||||||
|
selected_chain_id: string;
|
||||||
|
allowed_primitives: AssistantMcpDiscoveryPrimitive[];
|
||||||
|
rejected_primitives: string[];
|
||||||
|
execution_budget: AssistantMcpDiscoveryPlanContract["execution_budget"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssistantEvidenceCoverageGateContract {
|
||||||
|
requires_evidence_gate: true;
|
||||||
|
expected_coverage: AssistantEvidenceCoverageExpectation;
|
||||||
|
answer_permission_if_satisfied: AssistantEvidenceAnswerMode;
|
||||||
|
answer_may_use_raw_model_claims: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssistantEvidenceAnswerContract {
|
||||||
|
answer_mode: AssistantEvidenceAnswerMode;
|
||||||
|
required_user_layers: string[];
|
||||||
|
forbidden_overclaim_flags: string[];
|
||||||
|
must_keep_internal_mechanics_hidden: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssistantEvidencePlannerContract {
|
||||||
|
schema_version: typeof ASSISTANT_EVIDENCE_PLANNER_SCHEMA_VERSION;
|
||||||
|
policy_owner: "assistantEvidencePlanner";
|
||||||
|
planner_status: AssistantEvidencePlannerStatus;
|
||||||
|
semantic_data_need: string | null;
|
||||||
|
selected_chain_id: string;
|
||||||
|
data_need: AssistantEvidenceDataNeedContract;
|
||||||
|
evidence_axes: AssistantEvidenceAxesContract;
|
||||||
|
primitive_plan: AssistantEvidencePrimitivePlanContract;
|
||||||
|
coverage_gate: AssistantEvidenceCoverageGateContract;
|
||||||
|
answer_contract: AssistantEvidenceAnswerContract;
|
||||||
|
reason_codes: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BuildAssistantEvidencePlannerInput {
|
||||||
|
selectedChainId: string;
|
||||||
|
plannerStatus: AssistantEvidencePlannerStatus;
|
||||||
|
semanticDataNeed?: string | null;
|
||||||
|
dataNeedGraph?: AssistantMcpDiscoveryDataNeedGraphContract | null;
|
||||||
|
discoveryPlan: AssistantMcpDiscoveryPlanContract;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toNonEmptyString(value: unknown): string | null {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const text = String(value).trim();
|
||||||
|
return text.length > 0 ? text : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeReasonCode(value: string): string | null {
|
||||||
|
const normalized = value
|
||||||
|
.trim()
|
||||||
|
.replace(/[^\p{L}\p{N}_.:-]+/gu, "_")
|
||||||
|
.replace(/^_+|_+$/g, "")
|
||||||
|
.toLowerCase();
|
||||||
|
return normalized.length > 0 ? normalized.slice(0, 120) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pushReason(target: string[], value: string): void {
|
||||||
|
const normalized = normalizeReasonCode(value);
|
||||||
|
if (normalized && !target.includes(normalized)) {
|
||||||
|
target.push(normalized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function uniqueStrings(values: unknown[]): string[] {
|
||||||
|
const result: string[] = [];
|
||||||
|
for (const value of values) {
|
||||||
|
const text = toNonEmptyString(value);
|
||||||
|
if (text && !result.includes(text)) {
|
||||||
|
result.push(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function providedAxesFromMeaning(meaning: AssistantMcpDiscoveryTurnMeaningRef | null): string[] {
|
||||||
|
const result: string[] = [];
|
||||||
|
if ((meaning?.explicit_entity_candidates?.length ?? 0) > 0) {
|
||||||
|
result.push("counterparty");
|
||||||
|
result.push("business_entity");
|
||||||
|
}
|
||||||
|
if (toNonEmptyString(meaning?.explicit_organization_scope)) {
|
||||||
|
result.push("organization");
|
||||||
|
}
|
||||||
|
if (toNonEmptyString(meaning?.explicit_date_scope)) {
|
||||||
|
result.push("period");
|
||||||
|
}
|
||||||
|
if (toNonEmptyString(meaning?.asked_aggregation_axis)) {
|
||||||
|
result.push("aggregate_axis");
|
||||||
|
}
|
||||||
|
if (toNonEmptyString(meaning?.metadata_scope_hint)) {
|
||||||
|
result.push("metadata_scope");
|
||||||
|
}
|
||||||
|
return uniqueStrings(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function missingAxes(requiredAxes: string[], providedAxes: string[]): string[] {
|
||||||
|
return requiredAxes.filter((axis) => !providedAxes.includes(axis));
|
||||||
|
}
|
||||||
|
|
||||||
|
function coverageExpectationFor(
|
||||||
|
graph: AssistantMcpDiscoveryDataNeedGraphContract | null
|
||||||
|
): AssistantEvidenceCoverageExpectation {
|
||||||
|
if (graph?.proof_expectation === "bounded_inference") {
|
||||||
|
return "bounded_inference";
|
||||||
|
}
|
||||||
|
if (graph?.proof_expectation === "clarification_required") {
|
||||||
|
return "clarification";
|
||||||
|
}
|
||||||
|
return "confirmed_coverage";
|
||||||
|
}
|
||||||
|
|
||||||
|
function answerModeFor(input: {
|
||||||
|
plannerStatus: AssistantEvidencePlannerStatus;
|
||||||
|
coverageExpectation: AssistantEvidenceCoverageExpectation;
|
||||||
|
}): AssistantEvidenceAnswerMode {
|
||||||
|
if (input.plannerStatus === "needs_clarification") {
|
||||||
|
return "clarification_required";
|
||||||
|
}
|
||||||
|
if (input.plannerStatus === "blocked") {
|
||||||
|
return "checked_sources_only";
|
||||||
|
}
|
||||||
|
if (input.coverageExpectation === "bounded_inference") {
|
||||||
|
return "bounded_business_inference";
|
||||||
|
}
|
||||||
|
if (input.coverageExpectation === "clarification") {
|
||||||
|
return "clarification_required";
|
||||||
|
}
|
||||||
|
return "confirmed_business_answer";
|
||||||
|
}
|
||||||
|
|
||||||
|
function requiredUserLayersFor(answerMode: AssistantEvidenceAnswerMode): string[] {
|
||||||
|
if (answerMode === "clarification_required") {
|
||||||
|
return ["clarifying_question", "why_needed", "available_calculation"];
|
||||||
|
}
|
||||||
|
if (answerMode === "bounded_business_inference") {
|
||||||
|
return ["business_conclusion", "key_figures", "evidence_boundary", "next_step"];
|
||||||
|
}
|
||||||
|
if (answerMode === "checked_sources_only") {
|
||||||
|
return ["checked_sources_boundary", "unknowns", "next_probe"];
|
||||||
|
}
|
||||||
|
return ["direct_business_answer", "key_figures", "evidence_boundary", "next_step"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildAssistantEvidencePlanner(
|
||||||
|
input: BuildAssistantEvidencePlannerInput
|
||||||
|
): AssistantEvidencePlannerContract {
|
||||||
|
const graph = input.dataNeedGraph ?? null;
|
||||||
|
const plan = input.discoveryPlan;
|
||||||
|
const turnMeaning = plan.turn_meaning_ref;
|
||||||
|
const requiredAxes = uniqueStrings(plan.required_axes);
|
||||||
|
const providedAxes = providedAxesFromMeaning(turnMeaning);
|
||||||
|
const graphClarificationGaps = uniqueStrings(graph?.clarification_gaps ?? []);
|
||||||
|
const axisGaps = missingAxes(requiredAxes, providedAxes);
|
||||||
|
const clarificationGaps = uniqueStrings([...graphClarificationGaps, ...axisGaps]);
|
||||||
|
const coverageExpectation = coverageExpectationFor(graph);
|
||||||
|
const answerMode = answerModeFor({
|
||||||
|
plannerStatus: input.plannerStatus,
|
||||||
|
coverageExpectation
|
||||||
|
});
|
||||||
|
const reasonCodes = uniqueStrings(plan.reason_codes);
|
||||||
|
|
||||||
|
pushReason(reasonCodes, "evidence_planner_contract_built");
|
||||||
|
if (graph) {
|
||||||
|
pushReason(reasonCodes, "evidence_planner_consumed_data_need_graph");
|
||||||
|
}
|
||||||
|
if (clarificationGaps.length > 0) {
|
||||||
|
pushReason(reasonCodes, "evidence_planner_has_missing_axes_or_gaps");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
schema_version: ASSISTANT_EVIDENCE_PLANNER_SCHEMA_VERSION,
|
||||||
|
policy_owner: "assistantEvidencePlanner",
|
||||||
|
planner_status: input.plannerStatus,
|
||||||
|
semantic_data_need: toNonEmptyString(input.semanticDataNeed),
|
||||||
|
selected_chain_id: input.selectedChainId,
|
||||||
|
data_need: {
|
||||||
|
business_fact_family: graph?.business_fact_family ?? null,
|
||||||
|
action_family: graph?.action_family ?? null,
|
||||||
|
aggregation_need: graph?.aggregation_need ?? null,
|
||||||
|
comparison_need: graph?.comparison_need ?? null,
|
||||||
|
ranking_need: graph?.ranking_need ?? null,
|
||||||
|
time_scope_need: graph?.time_scope_need ?? null,
|
||||||
|
proof_expectation: graph?.proof_expectation ?? null,
|
||||||
|
subject_candidates: uniqueStrings(graph?.subject_candidates ?? [])
|
||||||
|
},
|
||||||
|
evidence_axes: {
|
||||||
|
required_axes: requiredAxes,
|
||||||
|
provided_axes: providedAxes,
|
||||||
|
missing_axes: axisGaps,
|
||||||
|
clarification_gaps: clarificationGaps
|
||||||
|
},
|
||||||
|
primitive_plan: {
|
||||||
|
selected_chain_id: input.selectedChainId,
|
||||||
|
allowed_primitives: plan.allowed_primitives,
|
||||||
|
rejected_primitives: plan.rejected_primitives,
|
||||||
|
execution_budget: plan.execution_budget
|
||||||
|
},
|
||||||
|
coverage_gate: {
|
||||||
|
requires_evidence_gate: true,
|
||||||
|
expected_coverage: coverageExpectation,
|
||||||
|
answer_permission_if_satisfied: answerMode,
|
||||||
|
answer_may_use_raw_model_claims: false
|
||||||
|
},
|
||||||
|
answer_contract: {
|
||||||
|
answer_mode: answerMode,
|
||||||
|
required_user_layers: requiredUserLayersFor(answerMode),
|
||||||
|
forbidden_overclaim_flags: uniqueStrings(graph?.forbidden_overclaim_flags ?? []),
|
||||||
|
must_keep_internal_mechanics_hidden: true
|
||||||
|
},
|
||||||
|
reason_codes: reasonCodes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import type { AssistantMcpDiscoveryRuntimeEntryPointContract } from "./assistantMcpDiscoveryRuntimeEntryPoint";
|
import type { AssistantMcpDiscoveryRuntimeEntryPointContract } from "./assistantMcpDiscoveryRuntimeEntryPoint";
|
||||||
import type { AssistantMcpRouteCandidateContract } from "./assistantMcpDiscoveryRuntimeBridge";
|
import type { AssistantMcpRouteCandidateContract } from "./assistantMcpDiscoveryRuntimeBridge";
|
||||||
|
import type { AssistantEvidencePlannerContract } from "./assistantEvidencePlanner";
|
||||||
|
|
||||||
export interface AssistantMcpDiscoveryDebugAttachmentFields {
|
export interface AssistantMcpDiscoveryDebugAttachmentFields {
|
||||||
assistant_mcp_discovery_entry_point_v1: AssistantMcpDiscoveryRuntimeEntryPointContract | null;
|
assistant_mcp_discovery_entry_point_v1: AssistantMcpDiscoveryRuntimeEntryPointContract | null;
|
||||||
|
|
@ -8,6 +9,11 @@ export interface AssistantMcpDiscoveryDebugAttachmentFields {
|
||||||
mcp_discovery_hot_runtime_wired: false;
|
mcp_discovery_hot_runtime_wired: false;
|
||||||
mcp_discovery_bridge_status: string | null;
|
mcp_discovery_bridge_status: string | null;
|
||||||
mcp_discovery_selected_chain_id: string | null;
|
mcp_discovery_selected_chain_id: string | null;
|
||||||
|
mcp_discovery_evidence_plan_v1: AssistantEvidencePlannerContract | null;
|
||||||
|
mcp_discovery_evidence_plan_status: string | null;
|
||||||
|
mcp_discovery_evidence_plan_answer_mode: string | null;
|
||||||
|
mcp_discovery_evidence_plan_missing_axes: string[];
|
||||||
|
mcp_discovery_evidence_plan_expected_coverage: string | null;
|
||||||
mcp_discovery_catalog_chain_template_matches: string[];
|
mcp_discovery_catalog_chain_template_matches: string[];
|
||||||
mcp_discovery_catalog_chain_alignment_status: string | null;
|
mcp_discovery_catalog_chain_alignment_status: string | null;
|
||||||
mcp_discovery_catalog_chain_top_match: string | null;
|
mcp_discovery_catalog_chain_top_match: string | null;
|
||||||
|
|
@ -78,6 +84,11 @@ function isRouteCandidateContract(value: unknown): value is AssistantMcpRouteCan
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isEvidencePlannerContract(value: unknown): value is AssistantEvidencePlannerContract {
|
||||||
|
const record = toRecordObject(value);
|
||||||
|
return record?.schema_version === "assistant_evidence_planner_v1" && record?.policy_owner === "assistantEvidencePlanner";
|
||||||
|
}
|
||||||
|
|
||||||
function resolveEntryPoint(input: AttachAssistantMcpDiscoveryDebugInput): AssistantMcpDiscoveryRuntimeEntryPointContract | null {
|
function resolveEntryPoint(input: AttachAssistantMcpDiscoveryDebugInput): AssistantMcpDiscoveryRuntimeEntryPointContract | null {
|
||||||
if (isMcpDiscoveryEntryPointContract(input.entryPoint)) {
|
if (isMcpDiscoveryEntryPointContract(input.entryPoint)) {
|
||||||
return input.entryPoint;
|
return input.entryPoint;
|
||||||
|
|
@ -95,6 +106,10 @@ export function buildAssistantMcpDiscoveryDebugAttachmentFields(
|
||||||
const entryPoint = resolveEntryPoint(input);
|
const entryPoint = resolveEntryPoint(input);
|
||||||
const bridge = toRecordObject(entryPoint?.bridge);
|
const bridge = toRecordObject(entryPoint?.bridge);
|
||||||
const planner = toRecordObject(bridge?.planner);
|
const planner = toRecordObject(bridge?.planner);
|
||||||
|
const evidencePlan = isEvidencePlannerContract(planner?.evidence_plan) ? planner.evidence_plan : null;
|
||||||
|
const evidencePlanAxes = toRecordObject(evidencePlan?.evidence_axes);
|
||||||
|
const evidencePlanCoverageGate = toRecordObject(evidencePlan?.coverage_gate);
|
||||||
|
const evidencePlanAnswerContract = toRecordObject(evidencePlan?.answer_contract);
|
||||||
const chainAlignment = toRecordObject(planner?.catalog_chain_template_alignment);
|
const chainAlignment = toRecordObject(planner?.catalog_chain_template_alignment);
|
||||||
const routeCandidate = isRouteCandidateContract(bridge?.route_candidate) ? bridge.route_candidate : null;
|
const routeCandidate = isRouteCandidateContract(bridge?.route_candidate) ? bridge.route_candidate : null;
|
||||||
const answerDraft = toRecordObject(bridge?.answer_draft);
|
const answerDraft = toRecordObject(bridge?.answer_draft);
|
||||||
|
|
@ -106,6 +121,11 @@ export function buildAssistantMcpDiscoveryDebugAttachmentFields(
|
||||||
mcp_discovery_hot_runtime_wired: false,
|
mcp_discovery_hot_runtime_wired: false,
|
||||||
mcp_discovery_bridge_status: toNonEmptyString(bridge?.bridge_status),
|
mcp_discovery_bridge_status: toNonEmptyString(bridge?.bridge_status),
|
||||||
mcp_discovery_selected_chain_id: toNonEmptyString(planner?.selected_chain_id),
|
mcp_discovery_selected_chain_id: toNonEmptyString(planner?.selected_chain_id),
|
||||||
|
mcp_discovery_evidence_plan_v1: evidencePlan,
|
||||||
|
mcp_discovery_evidence_plan_status: toNonEmptyString(evidencePlan?.planner_status),
|
||||||
|
mcp_discovery_evidence_plan_answer_mode: toNonEmptyString(evidencePlanAnswerContract?.answer_mode),
|
||||||
|
mcp_discovery_evidence_plan_missing_axes: toStringArray(evidencePlanAxes?.missing_axes),
|
||||||
|
mcp_discovery_evidence_plan_expected_coverage: toNonEmptyString(evidencePlanCoverageGate?.expected_coverage),
|
||||||
mcp_discovery_catalog_chain_template_matches: toStringArray(planner?.catalog_chain_template_matches),
|
mcp_discovery_catalog_chain_template_matches: toStringArray(planner?.catalog_chain_template_matches),
|
||||||
mcp_discovery_catalog_chain_alignment_status: toNonEmptyString(chainAlignment?.alignment_status),
|
mcp_discovery_catalog_chain_alignment_status: toNonEmptyString(chainAlignment?.alignment_status),
|
||||||
mcp_discovery_catalog_chain_top_match: toNonEmptyString(chainAlignment?.top_chain_template_match),
|
mcp_discovery_catalog_chain_top_match: toNonEmptyString(chainAlignment?.top_chain_template_match),
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ import {
|
||||||
type AssistantMcpCatalogPlanReview
|
type AssistantMcpCatalogPlanReview
|
||||||
} from "./assistantMcpCatalogIndex";
|
} from "./assistantMcpCatalogIndex";
|
||||||
import type { AssistantMcpDiscoveryDataNeedGraphContract } from "./assistantMcpDiscoveryDataNeedGraph";
|
import type { AssistantMcpDiscoveryDataNeedGraphContract } from "./assistantMcpDiscoveryDataNeedGraph";
|
||||||
|
import {
|
||||||
|
buildAssistantEvidencePlanner,
|
||||||
|
type AssistantEvidencePlannerContract
|
||||||
|
} from "./assistantEvidencePlanner";
|
||||||
|
|
||||||
export const ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION = "assistant_mcp_discovery_planner_v1" as const;
|
export const ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION = "assistant_mcp_discovery_planner_v1" as const;
|
||||||
|
|
||||||
|
|
@ -91,6 +95,7 @@ export interface AssistantMcpDiscoveryPlannerContract {
|
||||||
catalog_chain_template_alignment: AssistantMcpDiscoveryCatalogChainTemplateAlignment;
|
catalog_chain_template_alignment: AssistantMcpDiscoveryCatalogChainTemplateAlignment;
|
||||||
proposed_primitives: AssistantMcpDiscoveryPrimitive[];
|
proposed_primitives: AssistantMcpDiscoveryPrimitive[];
|
||||||
required_axes: string[];
|
required_axes: string[];
|
||||||
|
evidence_plan: AssistantEvidencePlannerContract;
|
||||||
discovery_plan: AssistantMcpDiscoveryPlanContract;
|
discovery_plan: AssistantMcpDiscoveryPlanContract;
|
||||||
catalog_review: AssistantMcpCatalogPlanReview;
|
catalog_review: AssistantMcpCatalogPlanReview;
|
||||||
reason_codes: string[];
|
reason_codes: string[];
|
||||||
|
|
@ -1422,6 +1427,13 @@ export function planAssistantMcpDiscovery(
|
||||||
} else {
|
} else {
|
||||||
pushReason(reasonCodes, "planner_needs_more_user_or_scope_context");
|
pushReason(reasonCodes, "planner_needs_more_user_or_scope_context");
|
||||||
}
|
}
|
||||||
|
const evidencePlan = buildAssistantEvidencePlanner({
|
||||||
|
selectedChainId: recipe.chainId,
|
||||||
|
plannerStatus,
|
||||||
|
semanticDataNeed,
|
||||||
|
dataNeedGraph,
|
||||||
|
discoveryPlan: plan
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
schema_version: ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION,
|
schema_version: ASSISTANT_MCP_DISCOVERY_PLANNER_SCHEMA_VERSION,
|
||||||
|
|
@ -1436,6 +1448,7 @@ export function planAssistantMcpDiscovery(
|
||||||
catalog_chain_template_alignment: catalogChainTemplateAlignment,
|
catalog_chain_template_alignment: catalogChainTemplateAlignment,
|
||||||
proposed_primitives: recipe.primitives,
|
proposed_primitives: recipe.primitives,
|
||||||
required_axes: recipe.axes,
|
required_axes: recipe.axes,
|
||||||
|
evidence_plan: evidencePlan,
|
||||||
discovery_plan: plan,
|
discovery_plan: plan,
|
||||||
catalog_review: adjustedReview,
|
catalog_review: adjustedReview,
|
||||||
reason_codes: reasonCodes
|
reason_codes: reasonCodes
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,21 @@ function entryPointContract(overrides: Record<string, unknown> = {}) {
|
||||||
requires_user_clarification: false,
|
requires_user_clarification: false,
|
||||||
planner: {
|
planner: {
|
||||||
selected_chain_id: "value_flow_ranking",
|
selected_chain_id: "value_flow_ranking",
|
||||||
|
evidence_plan: {
|
||||||
|
schema_version: "assistant_evidence_planner_v1",
|
||||||
|
policy_owner: "assistantEvidencePlanner",
|
||||||
|
planner_status: "ready_for_execution",
|
||||||
|
selected_chain_id: "value_flow_ranking",
|
||||||
|
evidence_axes: {
|
||||||
|
missing_axes: []
|
||||||
|
},
|
||||||
|
coverage_gate: {
|
||||||
|
expected_coverage: "confirmed_coverage"
|
||||||
|
},
|
||||||
|
answer_contract: {
|
||||||
|
answer_mode: "confirmed_business_answer"
|
||||||
|
}
|
||||||
|
},
|
||||||
catalog_chain_template_matches: ["value_flow_ranking", "value_flow"],
|
catalog_chain_template_matches: ["value_flow_ranking", "value_flow"],
|
||||||
catalog_chain_template_alignment: {
|
catalog_chain_template_alignment: {
|
||||||
alignment_status: "selected_matches_top",
|
alignment_status: "selected_matches_top",
|
||||||
|
|
@ -69,6 +84,10 @@ describe("assistant MCP discovery debug attachment", () => {
|
||||||
expect(debug.mcp_discovery_hot_runtime_wired).toBe(false);
|
expect(debug.mcp_discovery_hot_runtime_wired).toBe(false);
|
||||||
expect(debug.mcp_discovery_bridge_status).toBe("answer_draft_ready");
|
expect(debug.mcp_discovery_bridge_status).toBe("answer_draft_ready");
|
||||||
expect(debug.mcp_discovery_selected_chain_id).toBe("value_flow_ranking");
|
expect(debug.mcp_discovery_selected_chain_id).toBe("value_flow_ranking");
|
||||||
|
expect(debug.mcp_discovery_evidence_plan_status).toBe("ready_for_execution");
|
||||||
|
expect(debug.mcp_discovery_evidence_plan_answer_mode).toBe("confirmed_business_answer");
|
||||||
|
expect(debug.mcp_discovery_evidence_plan_expected_coverage).toBe("confirmed_coverage");
|
||||||
|
expect(debug.mcp_discovery_evidence_plan_missing_axes).toEqual([]);
|
||||||
expect(debug.mcp_discovery_catalog_chain_template_matches).toEqual(["value_flow_ranking", "value_flow"]);
|
expect(debug.mcp_discovery_catalog_chain_template_matches).toEqual(["value_flow_ranking", "value_flow"]);
|
||||||
expect(debug.mcp_discovery_catalog_chain_alignment_status).toBe("selected_matches_top");
|
expect(debug.mcp_discovery_catalog_chain_alignment_status).toBe("selected_matches_top");
|
||||||
expect(debug.mcp_discovery_catalog_chain_top_match).toBe("value_flow_ranking");
|
expect(debug.mcp_discovery_catalog_chain_top_match).toBe("value_flow_ranking");
|
||||||
|
|
@ -100,6 +119,11 @@ describe("assistant MCP discovery debug attachment", () => {
|
||||||
expect(debug.mcp_discovery_hot_runtime_wired).toBe(false);
|
expect(debug.mcp_discovery_hot_runtime_wired).toBe(false);
|
||||||
expect(debug.mcp_discovery_bridge_status).toBeNull();
|
expect(debug.mcp_discovery_bridge_status).toBeNull();
|
||||||
expect(debug.mcp_discovery_selected_chain_id).toBeNull();
|
expect(debug.mcp_discovery_selected_chain_id).toBeNull();
|
||||||
|
expect(debug.mcp_discovery_evidence_plan_v1).toBeNull();
|
||||||
|
expect(debug.mcp_discovery_evidence_plan_status).toBeNull();
|
||||||
|
expect(debug.mcp_discovery_evidence_plan_answer_mode).toBeNull();
|
||||||
|
expect(debug.mcp_discovery_evidence_plan_expected_coverage).toBeNull();
|
||||||
|
expect(debug.mcp_discovery_evidence_plan_missing_axes).toEqual([]);
|
||||||
expect(debug.mcp_discovery_catalog_chain_template_matches).toEqual([]);
|
expect(debug.mcp_discovery_catalog_chain_template_matches).toEqual([]);
|
||||||
expect(debug.mcp_discovery_catalog_chain_alignment_status).toBeNull();
|
expect(debug.mcp_discovery_catalog_chain_alignment_status).toBeNull();
|
||||||
expect(debug.mcp_discovery_catalog_chain_top_match).toBeNull();
|
expect(debug.mcp_discovery_catalog_chain_top_match).toBeNull();
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,34 @@ describe("assistant MCP discovery planner", () => {
|
||||||
expect(result.required_axes).toEqual(["counterparty", "period", "aggregate_axis", "amount", "coverage_target"]);
|
expect(result.required_axes).toEqual(["counterparty", "period", "aggregate_axis", "amount", "coverage_target"]);
|
||||||
expect(result.catalog_review.review_status).toBe("catalog_compatible");
|
expect(result.catalog_review.review_status).toBe("catalog_compatible");
|
||||||
expect(result.discovery_plan.answer_may_use_raw_model_claims).toBe(false);
|
expect(result.discovery_plan.answer_may_use_raw_model_claims).toBe(false);
|
||||||
|
expect(result.evidence_plan).toMatchObject({
|
||||||
|
schema_version: "assistant_evidence_planner_v1",
|
||||||
|
planner_status: "ready_for_execution",
|
||||||
|
selected_chain_id: "value_flow",
|
||||||
|
data_need: {
|
||||||
|
business_fact_family: "value_flow",
|
||||||
|
action_family: "turnover",
|
||||||
|
proof_expectation: "coverage_checked_fact"
|
||||||
|
},
|
||||||
|
coverage_gate: {
|
||||||
|
expected_coverage: "confirmed_coverage",
|
||||||
|
answer_permission_if_satisfied: "confirmed_business_answer",
|
||||||
|
answer_may_use_raw_model_claims: false
|
||||||
|
},
|
||||||
|
answer_contract: {
|
||||||
|
answer_mode: "confirmed_business_answer",
|
||||||
|
must_keep_internal_mechanics_hidden: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(result.evidence_plan.evidence_axes.required_axes).toEqual([
|
||||||
|
"counterparty",
|
||||||
|
"period",
|
||||||
|
"aggregate_axis",
|
||||||
|
"amount",
|
||||||
|
"coverage_target"
|
||||||
|
]);
|
||||||
|
expect(result.evidence_plan.evidence_axes.provided_axes).toEqual(["counterparty", "business_entity", "period"]);
|
||||||
|
expect(result.evidence_plan.evidence_axes.missing_axes).toEqual(["aggregate_axis", "amount", "coverage_target"]);
|
||||||
expect(result.data_need_graph?.business_fact_family).toBe("value_flow");
|
expect(result.data_need_graph?.business_fact_family).toBe("value_flow");
|
||||||
expect(result.catalog_chain_template_matches[0]).toBe("value_flow");
|
expect(result.catalog_chain_template_matches[0]).toBe("value_flow");
|
||||||
expect(result.catalog_chain_template_alignment).toMatchObject({
|
expect(result.catalog_chain_template_alignment).toMatchObject({
|
||||||
|
|
@ -284,6 +312,17 @@ describe("assistant MCP discovery planner", () => {
|
||||||
selected_chain_matches_top: true
|
selected_chain_matches_top: true
|
||||||
});
|
});
|
||||||
expect(result.catalog_review.review_status).toBe("catalog_compatible");
|
expect(result.catalog_review.review_status).toBe("catalog_compatible");
|
||||||
|
expect(result.evidence_plan.coverage_gate.expected_coverage).toBe("bounded_inference");
|
||||||
|
expect(result.evidence_plan.answer_contract.answer_mode).toBe("bounded_business_inference");
|
||||||
|
expect(result.evidence_plan.answer_contract.required_user_layers).toEqual([
|
||||||
|
"business_conclusion",
|
||||||
|
"key_figures",
|
||||||
|
"evidence_boundary",
|
||||||
|
"next_step"
|
||||||
|
]);
|
||||||
|
expect(result.evidence_plan.answer_contract.forbidden_overclaim_flags).toContain(
|
||||||
|
"no_unchecked_business_health_claim"
|
||||||
|
);
|
||||||
expect(result.reason_codes).toContain("planner_selected_business_overview_from_data_need_graph");
|
expect(result.reason_codes).toContain("planner_selected_business_overview_from_data_need_graph");
|
||||||
expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_business_overview");
|
expect(result.reason_codes).toContain("planner_instantiated_catalog_chain_template_business_overview");
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue