NODEDC_1C/llm_normalizer/backend/tests/assistantMcpDiscoveryRuntim...

244 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { describe, expect, it, vi } from "vitest";
import { runAssistantMcpDiscoveryRuntimeEntryPoint } from "../src/services/assistantMcpDiscoveryRuntimeEntryPoint";
function buildDeps(rows: Array<Record<string, unknown>>, error: string | null = null) {
return {
executeAddressMcpQuery: vi.fn(async () => ({
fetched_rows: rows.length,
matched_rows: error ? 0 : rows.length,
raw_rows: rows,
rows: error ? [] : rows,
error
}))
};
}
function buildMetadataDeps(rows: Array<Record<string, unknown>>, error: string | null = null) {
return {
executeAddressMcpMetadata: vi.fn(async () => ({
fetched_rows: error ? 0 : rows.length,
raw_rows: error ? [] : rows,
rows: error ? [] : rows,
error
}))
};
}
describe("assistant MCP discovery runtime entry point", () => {
it("runs the bridge for discovery-eligible lifecycle turn context", async () => {
const result = await runAssistantMcpDiscoveryRuntimeEntryPoint({
userMessage: "Сколько лет мы работаем с Группа СВК?",
predecomposeContract: {
entities: { counterparty: "Группа СВК" },
period: {}
},
deps: buildDeps([{ Период: "2020-01-15T00:00:00", Регистратор: "CP_CUSTOMER_ACTIVITY_FIRST" }])
});
expect(result.entry_status).toBe("bridge_executed");
expect(result.hot_runtime_wired).toBe(false);
expect(result.discovery_attempted).toBe(true);
expect(result.turn_input.semantic_data_need).toBe("counterparty lifecycle evidence");
expect(result.bridge?.bridge_status).toBe("answer_draft_ready");
expect(result.bridge?.answer_draft.answer_mode).toBe("confirmed_with_bounded_inference");
expect(result.reason_codes).toContain("runtime_entry_point_bridge_executed");
});
it("skips supported exact turns before any discovery execution", async () => {
const deps = buildDeps([]);
const result = await runAssistantMcpDiscoveryRuntimeEntryPoint({
assistantTurnMeaning: {
asked_domain_family: "counterparty",
asked_action_family: "list_documents",
explicit_intent_candidate: "list_documents_by_counterparty",
explicit_entity_candidates: [{ value: "SVK" }]
},
deps
});
expect(result.entry_status).toBe("skipped_not_applicable");
expect(result.discovery_attempted).toBe(false);
expect(result.bridge).toBeNull();
expect(deps.executeAddressMcpQuery).not.toHaveBeenCalled();
expect(result.reason_codes).toContain("runtime_entry_point_skipped_supported_exact_turn");
});
it("passes unsupported-but-understood value turns into bridge with normalized entity scope", async () => {
const result = await runAssistantMcpDiscoveryRuntimeEntryPoint({
assistantTurnMeaning: {
asked_domain_family: "counterparty",
asked_action_family: "counterparty_value_or_turnover",
unsupported_but_understood_family: "counterparty_value_or_turnover",
explicit_entity_candidates: [{ value: "SVK" }]
},
predecomposeContract: {
entities: { counterparty: "Группа СВК" },
period: { period_from: "2020-01-01", period_to: "2020-12-31" }
},
deps: buildDeps([
{ Period: "2020-01-15T00:00:00", Amount: 1250, Counterparty: "SVK" },
{ Period: "2020-02-20T00:00:00", Amount: 2500, Counterparty: "SVK" }
])
});
expect(result.entry_status).toBe("bridge_executed");
expect(result.turn_input.turn_meaning_ref?.explicit_entity_candidates).toEqual(["SVK", "Группа СВК"]);
expect(result.bridge?.bridge_status).toBe("answer_draft_ready");
expect(result.bridge?.pilot.pilot_scope).toBe("counterparty_value_flow_query_movements_v1");
expect(result.bridge?.pilot.derived_value_flow?.total_amount).toBe(3750);
expect(result.bridge?.hot_runtime_wired).toBe(false);
expect(result.reason_codes).toContain("mcp_discovery_unsupported_but_understood_turn");
});
it("runs the bridge for raw metadata wording without an exact route owner", async () => {
const result = await runAssistantMcpDiscoveryRuntimeEntryPoint({
userMessage: "какие документы и поля есть в 1С по НДС?",
deps: buildMetadataDeps([
{
FullName: "Документ.СчетФактураВыданный",
MetaType: "Документ",
attributes: [{ Name: "Дата" }]
}
])
});
expect(result.entry_status).toBe("bridge_executed");
expect(result.discovery_attempted).toBe(true);
expect(result.turn_input.semantic_data_need).toBe("1C metadata evidence");
expect(result.turn_input.turn_meaning_ref).toMatchObject({
asked_domain_family: "metadata",
asked_action_family: "inspect_fields"
});
expect(result.bridge?.pilot.pilot_scope).toBe("metadata_inspection_v1");
expect(result.bridge?.answer_draft.headline).toContain("метаданным 1С");
});
it("runs the bridge for raw entity-resolution wording and executes the full grounding chain", async () => {
const result = await runAssistantMcpDiscoveryRuntimeEntryPoint({
userMessage: "найди в 1С контрагента Группа СВК",
deps: buildDeps([
{ Counterparty: "Группа СВК", CounterpartyRef: "Ref-1" },
{ Counterparty: "СВК Логистика", CounterpartyRef: "Ref-2" }
])
});
expect(result.entry_status).toBe("bridge_executed");
expect(result.discovery_attempted).toBe(true);
expect(result.turn_input.semantic_data_need).toBe("entity discovery evidence");
expect(result.turn_input.turn_meaning_ref).toMatchObject({
asked_domain_family: "entity_resolution",
asked_action_family: "search_business_entity",
explicit_entity_candidates: ["Группа СВК"]
});
expect(result.bridge?.bridge_status).toBe("answer_draft_ready");
expect(result.bridge?.pilot.pilot_scope).toBe("entity_resolution_search_v1");
expect(result.bridge?.pilot.executed_primitives).toEqual([
"search_business_entity",
"resolve_entity_reference",
"probe_coverage"
]);
expect(result.bridge?.pilot.derived_entity_resolution).toMatchObject({
resolution_status: "resolved",
resolved_entity: "Группа СВК",
resolved_reference: "Ref-1"
});
expect(result.bridge?.answer_draft.answer_mode).toBe("confirmed_with_bounded_inference");
});
it("runs the bridge again when the user clarifies one ambiguous entity-resolution candidate", async () => {
const result = await runAssistantMcpDiscoveryRuntimeEntryPoint({
userMessage: "СВК-А",
followupContext: {
previous_discovery_pilot_scope: "entity_resolution_search_v1",
previous_discovery_entity_resolution_status: "ambiguous",
previous_discovery_entity_candidates: ["СВК", "СВК-А", "СВК-Б"],
previous_discovery_entity_ambiguity_candidates: ["СВК-А", "СВК-Б"]
},
deps: buildDeps([
{ Counterparty: "СВК-А", CounterpartyRef: "Ref-1" },
{ Counterparty: "СВК-Б", CounterpartyRef: "Ref-2" }
])
});
expect(result.entry_status).toBe("bridge_executed");
expect(result.discovery_attempted).toBe(true);
expect(result.turn_input.source_signal).toBe("followup_context");
expect(result.turn_input.semantic_data_need).toBe("entity discovery evidence");
expect(result.turn_input.turn_meaning_ref).toMatchObject({
asked_domain_family: "entity_resolution",
asked_action_family: "search_business_entity",
explicit_entity_candidates: ["СВК-А"]
});
expect(result.bridge?.pilot.pilot_scope).toBe("entity_resolution_search_v1");
expect(result.bridge?.pilot.executed_primitives).toEqual([
"search_business_entity",
"resolve_entity_reference",
"probe_coverage"
]);
expect(result.bridge?.pilot.derived_entity_resolution).toMatchObject({
resolution_status: "resolved",
resolved_entity: "СВК-А",
resolved_reference: "Ref-1"
});
});
it("chains an ordinal ambiguity clarification directly into value-flow execution", async () => {
const result = await runAssistantMcpDiscoveryRuntimeEntryPoint({
userMessage: "второй вариант, сколько получили за 2020 год",
followupContext: {
previous_discovery_pilot_scope: "entity_resolution_search_v1",
previous_discovery_entity_resolution_status: "ambiguous",
previous_discovery_entity_candidates: ["СВК", "СВК-А", "СВК-Б"],
previous_discovery_entity_ambiguity_candidates: ["СВК-А", "СВК-Б"]
},
deps: buildDeps([
{ Period: "2020-01-15T00:00:00", Amount: 1200, Counterparty: "СВК-Б" },
{ Period: "2020-02-20T00:00:00", Amount: 800, Counterparty: "СВК-Б" }
])
});
expect(result.entry_status).toBe("bridge_executed");
expect(result.discovery_attempted).toBe(true);
expect(result.turn_input.source_signal).toBe("followup_context");
expect(result.turn_input.semantic_data_need).toBe("counterparty value-flow evidence");
expect(result.turn_input.turn_meaning_ref).toMatchObject({
asked_domain_family: "counterparty_value",
asked_action_family: "turnover",
explicit_entity_candidates: ["СВК-Б"],
explicit_date_scope: "2020"
});
expect(result.bridge?.pilot.pilot_scope).toBe("counterparty_value_flow_query_movements_v1");
expect(result.bridge?.pilot.derived_value_flow).toMatchObject({
counterparty: "СВК-Б",
period_scope: "2020",
total_amount: 2000
});
});
it("chains an ordinal ambiguity clarification directly into document evidence execution", async () => {
const result = await runAssistantMcpDiscoveryRuntimeEntryPoint({
userMessage: "первый вариант, покажи документы за 2020 год",
followupContext: {
previous_discovery_pilot_scope: "entity_resolution_search_v1",
previous_discovery_entity_resolution_status: "ambiguous",
previous_discovery_entity_candidates: ["СВК", "СВК-А", "СВК-Б"],
previous_discovery_entity_ambiguity_candidates: ["СВК-А", "СВК-Б"]
},
deps: buildDeps([{ Period: "2020-03-12T00:00:00", Counterparty: "СВК-А", Registrar: "Doc-1" }])
});
expect(result.entry_status).toBe("bridge_executed");
expect(result.discovery_attempted).toBe(true);
expect(result.turn_input.source_signal).toBe("followup_context");
expect(result.turn_input.semantic_data_need).toBe("document evidence");
expect(result.turn_input.turn_meaning_ref).toMatchObject({
asked_domain_family: "documents",
asked_action_family: "list_documents",
explicit_entity_candidates: ["СВК-А"],
explicit_date_scope: "2020"
});
expect(result.bridge?.pilot.pilot_scope).toBe("counterparty_document_evidence_query_documents_v1");
expect(result.bridge?.answer_draft.answer_mode).toBe("confirmed_with_bounded_inference");
});
});