diff --git a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryTurnInputAdapter.js b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryTurnInputAdapter.js index 998aae5..9ddc9e6 100644 --- a/llm_normalizer/backend/dist/services/assistantMcpDiscoveryTurnInputAdapter.js +++ b/llm_normalizer/backend/dist/services/assistantMcpDiscoveryTurnInputAdapter.js @@ -374,6 +374,12 @@ function buildAssistantMcpDiscoveryTurnInput(input) { !rawLifecycleSignal && !rawValueFlowSignal && hasDocumentEvidenceFollowupSignal(rawText)); + const metadataAmbiguityResolvedDocumentFollowupApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" && + followupSeed.metadataAmbiguityDetected && + followupSeed.counterparty && + !rawLifecycleSignal && + !rawValueFlowSignal && + hasDocumentEvidenceFollowupSignal(rawText)); const metadataGroundedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" && followupSeed.metadataRouteFamily === "movement_evidence" && !followupSeed.metadataAmbiguityDetected && @@ -381,6 +387,12 @@ function buildAssistantMcpDiscoveryTurnInput(input) { !rawLifecycleSignal && !rawValueFlowSignal && hasMovementEvidenceFollowupSignal(rawText)); + const metadataAmbiguityResolvedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" && + followupSeed.metadataAmbiguityDetected && + followupSeed.counterparty && + !rawLifecycleSignal && + !rawValueFlowSignal && + hasMovementEvidenceFollowupSignal(rawText)); const metadataGroundedLaneContinuationApplicable = Boolean(followupSeed.pilotScope === "metadata_inspection_v1" && (followupSeed.metadataRouteFamily === "document_evidence" || followupSeed.metadataRouteFamily === "movement_evidence") && @@ -393,8 +405,10 @@ function buildAssistantMcpDiscoveryTurnInput(input) { !hasMovementEvidenceFollowupSignal(rawText) && hasMetadataDownstreamContinuationSignal(rawText)); const metadataGroundedDocumentLaneApplicable = metadataGroundedDocumentFollowupApplicable || + metadataAmbiguityResolvedDocumentFollowupApplicable || (metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "document_evidence"); const metadataGroundedMovementLaneApplicable = metadataGroundedMovementFollowupApplicable || + metadataAmbiguityResolvedMovementFollowupApplicable || (metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "movement_evidence"); const effectiveMetadataFollowupSeedApplicable = metadataFollowupSeedApplicable && !metadataGroundedDocumentLaneApplicable && @@ -590,9 +604,15 @@ function buildAssistantMcpDiscoveryTurnInput(input) { if (metadataGroundedDocumentFollowupApplicable) { pushReason(reasonCodes, "mcp_discovery_metadata_grounded_document_followup"); } + if (metadataAmbiguityResolvedDocumentFollowupApplicable) { + pushReason(reasonCodes, "mcp_discovery_metadata_ambiguity_resolved_to_document_lane"); + } if (metadataGroundedMovementFollowupApplicable) { pushReason(reasonCodes, "mcp_discovery_metadata_grounded_movement_followup"); } + if (metadataAmbiguityResolvedMovementFollowupApplicable) { + pushReason(reasonCodes, "mcp_discovery_metadata_ambiguity_resolved_to_movement_lane"); + } if (metadataGroundedLaneContinuationApplicable) { pushReason(reasonCodes, "mcp_discovery_metadata_grounded_lane_continuation"); } diff --git a/llm_normalizer/backend/src/services/assistantMcpDiscoveryTurnInputAdapter.ts b/llm_normalizer/backend/src/services/assistantMcpDiscoveryTurnInputAdapter.ts index 250d757..3cbcd29 100644 --- a/llm_normalizer/backend/src/services/assistantMcpDiscoveryTurnInputAdapter.ts +++ b/llm_normalizer/backend/src/services/assistantMcpDiscoveryTurnInputAdapter.ts @@ -515,6 +515,14 @@ export function buildAssistantMcpDiscoveryTurnInput( !rawValueFlowSignal && hasDocumentEvidenceFollowupSignal(rawText) ); + const metadataAmbiguityResolvedDocumentFollowupApplicable = Boolean( + followupSeed.pilotScope === "metadata_inspection_v1" && + followupSeed.metadataAmbiguityDetected && + followupSeed.counterparty && + !rawLifecycleSignal && + !rawValueFlowSignal && + hasDocumentEvidenceFollowupSignal(rawText) + ); const metadataGroundedMovementFollowupApplicable = Boolean( followupSeed.pilotScope === "metadata_inspection_v1" && followupSeed.metadataRouteFamily === "movement_evidence" && @@ -524,6 +532,14 @@ export function buildAssistantMcpDiscoveryTurnInput( !rawValueFlowSignal && hasMovementEvidenceFollowupSignal(rawText) ); + const metadataAmbiguityResolvedMovementFollowupApplicable = Boolean( + followupSeed.pilotScope === "metadata_inspection_v1" && + followupSeed.metadataAmbiguityDetected && + followupSeed.counterparty && + !rawLifecycleSignal && + !rawValueFlowSignal && + hasMovementEvidenceFollowupSignal(rawText) + ); const metadataGroundedLaneContinuationApplicable = Boolean( followupSeed.pilotScope === "metadata_inspection_v1" && (followupSeed.metadataRouteFamily === "document_evidence" || @@ -539,9 +555,11 @@ export function buildAssistantMcpDiscoveryTurnInput( ); const metadataGroundedDocumentLaneApplicable = metadataGroundedDocumentFollowupApplicable || + metadataAmbiguityResolvedDocumentFollowupApplicable || (metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "document_evidence"); const metadataGroundedMovementLaneApplicable = metadataGroundedMovementFollowupApplicable || + metadataAmbiguityResolvedMovementFollowupApplicable || (metadataGroundedLaneContinuationApplicable && followupSeed.metadataRouteFamily === "movement_evidence"); const effectiveMetadataFollowupSeedApplicable = metadataFollowupSeedApplicable && @@ -752,9 +770,15 @@ export function buildAssistantMcpDiscoveryTurnInput( if (metadataGroundedDocumentFollowupApplicable) { pushReason(reasonCodes, "mcp_discovery_metadata_grounded_document_followup"); } + if (metadataAmbiguityResolvedDocumentFollowupApplicable) { + pushReason(reasonCodes, "mcp_discovery_metadata_ambiguity_resolved_to_document_lane"); + } if (metadataGroundedMovementFollowupApplicable) { pushReason(reasonCodes, "mcp_discovery_metadata_grounded_movement_followup"); } + if (metadataAmbiguityResolvedMovementFollowupApplicable) { + pushReason(reasonCodes, "mcp_discovery_metadata_ambiguity_resolved_to_movement_lane"); + } if (metadataGroundedLaneContinuationApplicable) { pushReason(reasonCodes, "mcp_discovery_metadata_grounded_lane_continuation"); } diff --git a/llm_normalizer/backend/tests/assistantMcpDiscoveryTurnInputAdapter.test.ts b/llm_normalizer/backend/tests/assistantMcpDiscoveryTurnInputAdapter.test.ts index 372b92e..ba08c19 100644 --- a/llm_normalizer/backend/tests/assistantMcpDiscoveryTurnInputAdapter.test.ts +++ b/llm_normalizer/backend/tests/assistantMcpDiscoveryTurnInputAdapter.test.ts @@ -361,6 +361,68 @@ describe("assistant MCP discovery turn input adapter", () => { expect(result.reason_codes).toContain("mcp_discovery_metadata_grounded_lane_continuation"); }); + it("resolves ambiguous metadata surface into document lane when the follow-up explicitly asks for documents", () => { + const result = buildAssistantMcpDiscoveryTurnInput({ + userMessage: "по документам", + followupContext: { + previous_discovery_pilot_scope: "metadata_inspection_v1", + previous_discovery_metadata_ambiguity_detected: true, + previous_filters: { + counterparty: "SVK", + period_from: "2020-01-01", + period_to: "2020-12-31" + }, + previous_anchor_type: "counterparty", + previous_anchor_value: "SVK" + } + }); + + expect(result.adapter_status).toBe("ready"); + expect(result.should_run_discovery).toBe(true); + expect(result.source_signal).toBe("followup_context"); + expect(result.semantic_data_need).toBe("document evidence"); + expect(result.turn_meaning_ref).toMatchObject({ + asked_domain_family: "documents", + asked_action_family: "list_documents", + explicit_entity_candidates: ["SVK"], + explicit_date_scope: "2020", + unsupported_but_understood_family: "document_evidence", + stale_replay_forbidden: true + }); + expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_resolved_to_document_lane"); + }); + + it("resolves ambiguous metadata surface into movement lane when the follow-up explicitly asks for movements", () => { + const result = buildAssistantMcpDiscoveryTurnInput({ + userMessage: "по движениям", + followupContext: { + previous_discovery_pilot_scope: "metadata_inspection_v1", + previous_discovery_metadata_ambiguity_detected: true, + previous_filters: { + counterparty: "SVK", + period_from: "2020-01-01", + period_to: "2020-12-31" + }, + previous_anchor_type: "counterparty", + previous_anchor_value: "SVK" + } + }); + + expect(result.adapter_status).toBe("ready"); + expect(result.should_run_discovery).toBe(true); + expect(result.source_signal).toBe("followup_context"); + expect(result.semantic_data_need).toBe("movement evidence"); + expect(result.turn_meaning_ref).toMatchObject({ + asked_domain_family: "movements", + asked_action_family: "list_movements", + explicit_entity_candidates: ["SVK"], + explicit_date_scope: "2020", + unsupported_but_understood_family: "movement_evidence", + stale_replay_forbidden: true + }); + expect(result.reason_codes).toContain("mcp_discovery_metadata_ambiguity_resolved_to_movement_lane"); + }); + it("switches the checked year on a short payout follow-up while keeping prior discovery counterparty", () => { const result = buildAssistantMcpDiscoveryTurnInput({ userMessage: "а теперь за 2021?",