ARCH: восстановить память discovery-ответов в living chat
This commit is contained in:
@@ -108,11 +108,45 @@ describe("assistantContinuityPolicy organization authority", () => {
|
||||
|
||||
expect(facts).toEqual({
|
||||
item: "Рабочая станция",
|
||||
counterparty: null,
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
scopedDate: "31.03.2020"
|
||||
});
|
||||
});
|
||||
|
||||
it("reads counterparty, organization and period from grounded MCP discovery fallback", () => {
|
||||
const facts = resolveAddressDebugContextFacts({
|
||||
execution_lane: "living_chat",
|
||||
mcp_discovery_response_applied: true,
|
||||
assistant_active_organization: "ООО Альтернатива Плюс",
|
||||
assistant_mcp_discovery_entry_point_v1: {
|
||||
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||
entry_status: "bridge_executed",
|
||||
turn_input: {
|
||||
turn_meaning_ref: {
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020"
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
business_fact_answer_allowed: true,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(facts).toEqual({
|
||||
item: null,
|
||||
counterparty: "Группа СВК",
|
||||
organization: "ООО Альтернатива Плюс",
|
||||
scopedDate: "2020"
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves navigation session context through one shared helper", () => {
|
||||
const state = resolveNavigationSessionContextState({
|
||||
session_context: {
|
||||
|
||||
@@ -366,6 +366,56 @@ describe("assistant living chat runtime adapter", () => {
|
||||
expect(executeLlmChat).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("builds deterministic memory recap for prior grounded MCP discovery counterparty context", async () => {
|
||||
const executeLlmChat = vi.fn(async () => "raw-llm");
|
||||
const input = buildRuntimeInput({
|
||||
userMessage: "а ты помнишь, что мы выяснили по свк?",
|
||||
modeDecision: { mode: "chat", reason: "memory_recap_followup_detected" },
|
||||
sessionItems: [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "living_chat",
|
||||
mcp_discovery_response_applied: true,
|
||||
assistant_mcp_discovery_entry_point_v1: {
|
||||
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||
entry_status: "bridge_executed",
|
||||
turn_input: {
|
||||
turn_meaning_ref: {
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020"
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
business_fact_answer_allowed: true,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference"
|
||||
},
|
||||
pilot: {
|
||||
pilot_scope: "counterparty_supplier_payout_query_movements_v1",
|
||||
derived_value_flow: {
|
||||
total_amount_human_ru: "43 763 351,53 руб."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
executeLlmChat
|
||||
});
|
||||
|
||||
const output = await runAssistantLivingChatRuntime(input);
|
||||
|
||||
expect(output.handled).toBe(true);
|
||||
expect(output.chatText).toContain("Группа СВК");
|
||||
expect(output.chatText).toContain("43 763 351,53 руб.");
|
||||
expect(output.debug?.living_chat_response_source).toBe("deterministic_memory_recap_contract");
|
||||
expect(executeLlmChat).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("uses continuity-backed active organization for organization-fact boundary even when session scope is empty", async () => {
|
||||
const executeLlmChat = vi.fn(async () => "raw-llm");
|
||||
const input = buildRuntimeInput({
|
||||
@@ -434,4 +484,50 @@ describe("assistant living chat runtime adapter", () => {
|
||||
expect(output.debug?.living_chat_response_source).toBe("deterministic_answer_inspection_contract");
|
||||
expect(executeLlmChat).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("builds deterministic answer inspection reply over grounded MCP discovery net answer", async () => {
|
||||
const executeLlmChat = vi.fn(async () => "raw-llm");
|
||||
const input = buildRuntimeInput({
|
||||
userMessage: "что ты имел в виду под нетто по свк?",
|
||||
modeDecision: { mode: "chat", reason: "answer_inspection_followup_detected" },
|
||||
sessionItems: [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "living_chat",
|
||||
mcp_discovery_response_applied: true,
|
||||
assistant_mcp_discovery_entry_point_v1: {
|
||||
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||
entry_status: "bridge_executed",
|
||||
turn_input: {
|
||||
turn_meaning_ref: {
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_date_scope: "2020"
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
business_fact_answer_allowed: true,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference"
|
||||
},
|
||||
pilot: {
|
||||
pilot_scope: "counterparty_bidirectional_value_flow_query_movements_v1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
executeLlmChat
|
||||
});
|
||||
|
||||
const output = await runAssistantLivingChatRuntime(input);
|
||||
|
||||
expect(output.handled).toBe(true);
|
||||
expect(output.chatText).toContain("Группа СВК");
|
||||
expect(output.chatText).toContain("Нетто");
|
||||
expect(output.debug?.living_chat_response_source).toBe("deterministic_answer_inspection_contract");
|
||||
expect(executeLlmChat).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -133,6 +133,50 @@ describe("assistantMemoryRecapPolicy", () => {
|
||||
expect(signals.contextualMemoryRecapFollowupDetected).toBe(false);
|
||||
});
|
||||
|
||||
it("detects contextual memory recap over prior grounded MCP discovery answer", () => {
|
||||
const signals = policy.resolveRouteMemorySignals({
|
||||
rawUserMessage: "а ты помнишь, что мы уже выяснили по свк?",
|
||||
repairedRawUserMessage: "",
|
||||
effectiveAddressUserMessage: "",
|
||||
repairedEffectiveAddressUserMessage: "",
|
||||
dataScopeMetaQuery: false,
|
||||
capabilityMetaQuery: false,
|
||||
dataRetrievalSignal: false,
|
||||
strongDataSignal: false,
|
||||
aggregateBusinessAnalyticsSignal: false,
|
||||
lastGroundedAddressDebug: null,
|
||||
hasPriorAddressDebug: true,
|
||||
sessionItems: [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "living_chat",
|
||||
mcp_discovery_response_applied: true,
|
||||
assistant_mcp_discovery_entry_point_v1: {
|
||||
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||
entry_status: "bridge_executed",
|
||||
turn_input: {
|
||||
turn_meaning_ref: {
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_date_scope: "2020"
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
business_fact_answer_allowed: true,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(signals.contextualMemoryRecapFollowupDetected).toBe(true);
|
||||
});
|
||||
|
||||
it("builds deterministic recap summary from recent selected-object facts", () => {
|
||||
const context = resolveAssistantLivingChatMemoryContext({
|
||||
modeDecisionReason: "memory_recap_followup_detected",
|
||||
@@ -278,4 +322,114 @@ describe("assistantMemoryRecapPolicy", () => {
|
||||
expect(reply).toContain("Рабочая станция");
|
||||
expect(reply).toContain("Покупатель");
|
||||
});
|
||||
|
||||
it("builds deterministic recap summary from grounded MCP discovery counterparty context", () => {
|
||||
const sessionItems = [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "living_chat",
|
||||
mcp_discovery_response_applied: true,
|
||||
assistant_mcp_discovery_entry_point_v1: {
|
||||
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||
entry_status: "bridge_executed",
|
||||
turn_input: {
|
||||
turn_meaning_ref: {
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_organization_scope: "ООО Альтернатива Плюс",
|
||||
explicit_date_scope: "2020"
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
business_fact_answer_allowed: true,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference"
|
||||
},
|
||||
pilot: {
|
||||
pilot_scope: "counterparty_bidirectional_value_flow_query_movements_v1",
|
||||
derived_bidirectional_value_flow: {
|
||||
net_amount_human_ru: "3 865 501,50 руб.",
|
||||
incoming_customer_revenue: {
|
||||
total_amount_human_ru: "47 628 853,03 руб."
|
||||
},
|
||||
outgoing_supplier_payout: {
|
||||
total_amount_human_ru: "43 763 351,53 руб."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
const context = resolveAssistantLivingChatMemoryContext({
|
||||
modeDecisionReason: "memory_recap_followup_detected",
|
||||
sessionItems
|
||||
});
|
||||
|
||||
const reply = buildAddressMemoryRecapReply({
|
||||
organization: null,
|
||||
addressDebug: context.lastMemoryAddressDebug,
|
||||
sessionItems,
|
||||
toNonEmptyString: (value: unknown) => {
|
||||
const text = String(value ?? "").trim();
|
||||
return text.length > 0 ? text : null;
|
||||
}
|
||||
});
|
||||
|
||||
expect(context.contextualMemoryRecapFollowup).toBe(true);
|
||||
expect(reply).toContain("Группа СВК");
|
||||
expect(reply).toContain("нетто");
|
||||
expect(reply).toContain("47 628 853,03 руб.");
|
||||
expect(reply).toContain("43 763 351,53 руб.");
|
||||
});
|
||||
|
||||
it("builds grounded answer inspection reply for MCP discovery net answer", () => {
|
||||
const context = resolveAssistantLivingChatMemoryContext({
|
||||
modeDecisionReason: "answer_inspection_followup_detected",
|
||||
sessionItems: [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "living_chat",
|
||||
mcp_discovery_response_applied: true,
|
||||
assistant_mcp_discovery_entry_point_v1: {
|
||||
schema_version: "assistant_mcp_discovery_runtime_entry_point_v1",
|
||||
entry_status: "bridge_executed",
|
||||
turn_input: {
|
||||
turn_meaning_ref: {
|
||||
explicit_entity_candidates: ["Группа СВК"],
|
||||
explicit_date_scope: "2020"
|
||||
}
|
||||
},
|
||||
bridge: {
|
||||
bridge_status: "answer_draft_ready",
|
||||
business_fact_answer_allowed: true,
|
||||
answer_draft: {
|
||||
answer_mode: "confirmed_with_bounded_inference"
|
||||
},
|
||||
pilot: {
|
||||
pilot_scope: "counterparty_bidirectional_value_flow_query_movements_v1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const reply = buildSelectedObjectAnswerInspectionReply({
|
||||
addressDebug: context.lastAnswerInspectionAddressDebug,
|
||||
toNonEmptyString: (value: unknown) => {
|
||||
const text = String(value ?? "").trim();
|
||||
return text.length > 0 ? text : null;
|
||||
}
|
||||
});
|
||||
|
||||
expect(context.contextualAnswerInspectionFollowup).toBe(true);
|
||||
expect(reply).toContain("Группа СВК");
|
||||
expect(reply).toContain("Нетто");
|
||||
expect(reply).toContain("проверенному периоду");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user