NODEDC_1C/llm_normalizer/backend/tests/assistantSoftPolicyReply.te...

180 lines
5.6 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { composeAssistantAnswer } from "../src/services/answerComposer";
import type { UnifiedRetrievalResult } from "../src/types/assistant";
function routeSummary() {
return {
mode: "deterministic_v2" as const,
message_in_scope: true,
scope_confidence: "high" as const,
planner: {
total_fragments: 1,
in_scope_fragments: 1,
out_of_scope_fragments: 0,
discarded_fragments: 0,
contains_multiple_tasks: false
},
decisions: [],
fallback: {
type: "none" as const,
message: null
}
};
}
describe("assistant soft policy reply", () => {
it("renders soft non-template reply for weak broad-partial envelope", () => {
const retrieval: UnifiedRetrievalResult = {
fragment_id: "F1",
requirement_ids: ["R1"],
route: "store_feature_risk",
status: "partial",
result_type: "summary",
items: [{ note: "weak candidate" }],
summary: {
broad_query_detected: true,
broad_result_flag: true,
minimum_evidence_failed: true,
narrowing_strength: "weak"
},
evidence: [],
why_included: [],
selection_reason: [],
risk_factors: [],
business_interpretation: [],
confidence: "low",
limitations: ["Weak evidence envelope"],
errors: []
};
const output = composeAssistantAnswer({
userMessage: "Покажи общую картину рисков и аномалий по документам.",
routeSummary: routeSummary(),
retrievalResults: [retrieval],
requirements: [
{
requirement_id: "R1",
source_fragment_id: "F1",
requirement_text: "общая картина рисков",
subject_tokens: [],
status: "partially_covered",
route: "store_feature_risk"
}
],
coverageReport: {
requirements_total: 1,
requirements_covered: 0,
requirements_uncovered: [],
requirements_partially_covered: ["R1"],
clarification_needed_for: [],
out_of_scope_requirements: []
},
groundingCheck: {
status: "partial",
route_subject_match: true,
missing_requirements: ["R1"],
reasons: ["insufficient_detail"],
why_included_summary: [],
selection_reason_summary: []
},
enableAnswerPolicyV11: true
});
expect(output.reply_type).toBe("partial_coverage");
expect(output.assistant_reply).toContain("Что могу сделать сейчас:");
expect(output.assistant_reply).toContain("Что пока не доказано:");
expect(output.assistant_reply).not.toContain("Что сломано:");
});
it("keeps structured sectioned reply for focused grounded envelope", () => {
const retrieval: UnifiedRetrievalResult = {
fragment_id: "F1",
requirement_ids: ["R1"],
route: "store_feature_risk",
status: "ok",
result_type: "summary",
items: [{ doc: "A-1" }],
summary: {
broad_query_detected: false,
broad_result_flag: false,
minimum_evidence_failed: false,
narrowing_strength: "strong"
},
evidence: [
{
evidence_id: "ev-1",
claim_ref: "requirement:R1",
source_type: "retrieval_item",
source_ref: {
schema_version: "evidence_source_ref_v1",
namespace: "snapshot_2020",
entity: "document",
id: "A-1",
period: "2020-06",
canonical_ref: "evidence_source_ref_v1|snapshot_2020|document|A-1|2020-06"
},
pointer: {
fragment_id: "F1",
route: "store_feature_risk",
source: {
namespace: "snapshot_2020",
entity: "document",
id: "A-1",
period: "2020-06"
}
},
evidence_kind: "fact",
mechanism_note: "Переход подтвержден документом и проводкой.",
confidence: "high",
payload: {
amount: 100
}
}
],
why_included: ["релевантная запись"],
selection_reason: ["сильное совпадение"],
risk_factors: [],
business_interpretation: [],
confidence: "high",
limitations: [],
errors: []
};
const output = composeAssistantAnswer({
userMessage: "Проверь документ A-1 за июнь 2020.",
routeSummary: routeSummary(),
retrievalResults: [retrieval],
requirements: [
{
requirement_id: "R1",
source_fragment_id: "F1",
requirement_text: "проверить документ",
subject_tokens: [],
status: "covered",
route: "store_feature_risk"
}
],
coverageReport: {
requirements_total: 1,
requirements_covered: 1,
requirements_uncovered: [],
requirements_partially_covered: [],
clarification_needed_for: [],
out_of_scope_requirements: []
},
groundingCheck: {
status: "grounded",
route_subject_match: true,
missing_requirements: [],
reasons: [],
why_included_summary: ["релевантная запись"],
selection_reason_summary: ["сильное совпадение"]
},
enableAnswerPolicyV11: true
});
expect(output.reply_type).toBe("factual_with_explanation");
expect(output.assistant_reply).toContain("Что сломано:");
expect(output.assistant_reply).toContain("Ограничения:");
});
});