NODEDC_1C/llm_normalizer/backend/dist/services/assistantEvidencePlanner.js

182 lines
6.8 KiB
JavaScript

"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));
}
const USER_ACTIONABLE_AXIS_SET = new Set([
"counterparty",
"business_entity",
"organization",
"period",
"as_of_date",
"item",
"supplier",
"buyer",
"warehouse",
"document",
"contract",
"metadata_scope",
"lane_family_choice"
]);
function userActionableMissingAxes(axes) {
return axes.filter((axis) => USER_ACTIONABLE_AXIS_SET.has(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 additionalAxisGaps = uniqueStrings(input.additionalMissingAxes ?? []).filter((axis) => !providedAxes.includes(axis) && (requiredAxes.includes(axis) || USER_ACTIONABLE_AXIS_SET.has(axis)));
const axisGaps = uniqueStrings([...additionalAxisGaps, ...missingAxes(requiredAxes, providedAxes)]);
const actionableAxisGaps = userActionableMissingAxes(axisGaps);
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,
user_actionable_missing_axes: actionableAxisGaps,
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
};
}