NODEDC_1C/llm_normalizer/backend/tests/assistantCoverageGrounding....

200 lines
6.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
checkGroundingForRequirements,
evaluateCoverageForRequirements,
extractRequirementsForRoute
} from "../src/services/assistantCoverageGrounding";
describe("assistant coverage-grounding module", () => {
it("extracts requirements from deterministic route summary", () => {
const extracted = extractRequirementsForRoute({
routeSummary: {
mode: "deterministic_v2",
message_in_scope: true,
scope_confidence: "high",
planner: {
total_fragments: 2,
in_scope_fragments: 2,
out_of_scope_fragments: 0,
discarded_fragments: 0,
contains_multiple_tasks: false
},
decisions: [
{
fragment_id: "F1",
route: "no_route",
no_route_reason: "insufficient_specificity",
reason: "missing anchor"
},
{
fragment_id: "F2",
route: "hybrid_store_plus_live",
reason: "ok route"
}
],
fallback: {
type: "none",
message: null
}
} as any,
userMessage: "base question",
fragmentTextById: new Map([
["F1", "need more details"],
["F2", "check account 60"]
]),
extractSubjectTokens: (text) => (text.includes("60") ? ["account_60"] : ["counterparty"])
});
expect(extracted.requirements).toHaveLength(2);
expect(extracted.requirements[0].status).toBe("clarification_needed");
expect(extracted.requirements[0].route).toBeNull();
expect(extracted.requirements[1].status).toBe("covered");
expect(extracted.requirements[1].route).toBe("hybrid_store_plus_live");
expect(extracted.byFragment.get("F2")).toEqual(["R2"]);
});
it("evaluates coverage from retrieval outcomes", () => {
const requirements = [
{
requirement_id: "R1",
source_fragment_id: "F1",
requirement_text: "req1",
subject_tokens: ["account_60"],
status: "covered" as const,
route: "hybrid_store_plus_live"
},
{
requirement_id: "R2",
source_fragment_id: "F2",
requirement_text: "req2",
subject_tokens: ["counterparty"],
status: "covered" as const,
route: "store_feature_risk"
}
];
const retrievalResults = [
{
fragment_id: "F1",
requirement_ids: ["R1"],
route: "hybrid_store_plus_live",
status: "ok",
result_type: "summary",
items: [],
summary: {},
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: "doc-1",
period: "2020-07",
canonical_ref: "evidence_source_ref_v1|snapshot_2020|document|doc-1|2020-07"
},
pointer: {
fragment_id: "F1",
route: "hybrid_store_plus_live",
source: {
namespace: "snapshot_2020",
entity: "document",
id: "doc-1",
period: "2020-07"
},
locator: {
field_path: "amount",
item_index: 0
}
},
evidence_kind: "mechanism_link",
mechanism_note: "ok",
confidence: "high",
limitation: null,
payload: {}
}
],
why_included: ["why"],
selection_reason: ["sel"],
risk_factors: [],
business_interpretation: [],
confidence: "high",
limitations: [],
errors: []
},
{
fragment_id: "F2",
requirement_ids: ["R2"],
route: "store_feature_risk",
status: "empty",
result_type: "summary",
items: [],
summary: {},
evidence: [],
why_included: [],
selection_reason: [],
risk_factors: [],
business_interpretation: [],
confidence: "low",
limitations: [],
errors: []
}
] as any;
const evaluation = evaluateCoverageForRequirements(requirements as any, retrievalResults);
expect(evaluation.coverage.requirements_total).toBe(2);
expect(evaluation.coverage.requirements_covered).toBe(1);
expect(evaluation.coverage.requirements_uncovered).toContain("R2");
expect(evaluation.requirements.find((item) => item.requirement_id === "R1")?.status).toBe("covered");
});
it("produces route mismatch grounding when critical subject token is absent", () => {
const grounded = checkGroundingForRequirements({
userMessage: "Проверь НДС цепочку",
requirements: [
{
requirement_id: "R1",
source_fragment_id: "F1",
requirement_text: "vat chain",
subject_tokens: ["nds"],
status: "covered",
route: "hybrid_store_plus_live"
}
] as any,
coverage: {
requirements_total: 1,
requirements_covered: 1,
requirements_uncovered: [],
requirements_partially_covered: [],
clarification_needed_for: [],
out_of_scope_requirements: []
},
retrievalResults: [
{
fragment_id: "F1",
requirement_ids: ["R1"],
route: "hybrid_store_plus_live",
status: "ok",
result_type: "summary",
items: [],
summary: { note: "no tax markers" },
evidence: [],
why_included: [],
selection_reason: [],
risk_factors: [],
business_interpretation: [],
confidence: "medium",
limitations: [],
errors: []
}
] as any,
extractSubjectTokens: () => ["nds"]
});
expect(grounded.status).toBe("route_mismatch_blocked");
expect(grounded.route_subject_match).toBe(false);
expect(grounded.reasons.some((item) => item.includes("Ключевые ориентиры вопроса"))).toBe(true);
});
});