NODEDC_1C/llm_normalizer/backend/tests/assistantRoutePolicy.test.ts

760 lines
32 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 } from "vitest";
import { createAssistantRoutePolicy } from "../src/services/assistantRoutePolicy";
function toNonEmptyString(value: unknown): string | null {
if (value === null || value === undefined) {
return null;
}
const text = String(value).trim();
return text.length > 0 ? text : null;
}
function normalizeOrganizationScopeValue(value: unknown): string | null {
const text = toNonEmptyString(value);
if (!text) {
return null;
}
return text.replace(/^"+|"+$/g, "").replace(/^'+|'+$/g, "");
}
function buildPolicy(overrides: Record<string, unknown> = {}) {
return createAssistantRoutePolicy({
repairAddressMojibake: (text: string) => text,
findLastGroundedAddressAnswerDebug: () => null,
findLastOrganizationClarificationAddressDebug: () => null,
mergeKnownOrganizations: (values: unknown[]) =>
Array.from(
new Set(
(Array.isArray(values) ? values : [])
.map((item) => normalizeOrganizationScopeValue(item))
.filter((item): item is string => Boolean(item))
)
),
normalizeOrganizationScopeValue,
resolveOrganizationSelectionFromMessage: () => null,
resolveMetaSignalSet: (input: {
rawUserMessage?: string;
repairedRawUserMessage?: string;
effectiveAddressUserMessage?: string;
repairedEffectiveAddressUserMessage?: string;
}) => {
const samples = [
input.rawUserMessage,
input.repairedRawUserMessage,
input.effectiveAddressUserMessage,
input.repairedEffectiveAddressUserMessage
].join(" ");
return {
dataScopeMetaQuery: /по какой компании|какая база|по каким конторам/i.test(samples),
capabilityMetaQuery: /что СС РјРѕР¶РµС€СЊ|что СС СѓРјРµРµС€СЊ/i.test(samples),
metaAnswerFollowupSignal: /это норм|что думаешь/i.test(samples),
answerInspectionFollowupSignal: /это ошибка|у тебя написано кто контрагент/i.test(samples)
};
},
resolveHardMetaMode: (input: {
dataScopeMetaQuery?: boolean;
capabilityMetaQuery?: boolean;
dataRetrievalSignal?: boolean;
}) =>
input.dataScopeMetaQuery
? "data_scope"
: input.capabilityMetaQuery && !input.dataRetrievalSignal
? "capability"
: null,
isMetaFollowupOverGroundedAnswer: () => false,
isAnswerInspectionFollowupOverGroundedAnswer: () => false,
hasDataRetrievalRequestSignal: () => false,
hasOrganizationFactLookupSignal: () => false,
hasOrganizationFactFollowupSignal: () => false,
hasAggregateBusinessAnalyticsSignal: () => false,
hasStandaloneAddressTopicSignal: () => false,
hasOpenContractsAddressSignal: () => false,
detectAddressQuestionMode: () => ({ mode: "unsupported", confidence: "low" }),
resolveAddressIntent: () => ({ intent: "unknown", confidence: "low" }),
toNonEmptyString,
hasStrictDeepInvestigationCue: () => false,
hasStrongDataIntentSignal: () => false,
hasAccountingSignal: () => false,
hasDangerOrCoercionSignal: () => false,
hasAddressFollowupContextSignal: () => false,
hasShortDebtMirrorFollowupSignal: () => false,
isInventorySelectedObjectIntent: (intent: unknown) => /inventory/i.test(String(intent ?? "")),
hasShortInventoryObjectFollowupSignal: () => false,
resolveRouteMemorySignals: () => ({
contextualHistoricalCapabilityFollowupDetected: false,
contextualMemoryRecapFollowupDetected: false
}),
findLastAddressAssistantItem: () => null,
hasAddressLlmPreDecomposeCandidate: () => false,
resolveAddressToolGateDecision: () => ({
runAddressLane: false,
decision: "skip_address_lane",
reason: "no_address_signal_after_l0"
}),
hasSameDateAccountFollowupSignalForPredecompose: () => false,
hasLooseAllTimeAddressLookupSignal: () => false,
hasDeepAnalysisPreferenceSignal: () => false,
hasDirectDeepAnalysisSignal: () => false,
shouldEmitOrganizationSelectionReply: () => false,
compactWhitespace: (text: string) => String(text ?? "").replace(/\s+/g, " ").trim(),
hasDeepSessionContinuationSignal: () => false,
resolveLivingAssistantModeDecision: (input: { addressLaneTriggered?: boolean }) =>
input.addressLaneTriggered
? { mode: "address_data", reason: "address_lane_triggered" }
: { mode: "chat", reason: "living_chat_signal_detected" },
resolveProviderExecutionState: (input: { useMock?: unknown; llmPreDecomposeReason?: unknown }) => {
const reason = String(input?.llmPreDecomposeReason ?? "");
return {
provider_mode: Boolean(input?.useMock) ? "mock" : "unknown",
normalized_provider: null,
use_mock: Boolean(input?.useMock),
llm_runtime_unavailable_detected: /missing api key|authentication|api key is missing/i.test(reason),
living_mode_forced_deep: Boolean(input?.useMock),
living_mode_forced_reason: Boolean(input?.useMock) ? "mock_mode_keeps_deep_pipeline" : null
};
},
...overrides
});
}
describe("assistantRoutePolicy", () => {
it("routes data-scope meta question to chat contract", () => {
const policy = buildPolicy();
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "РїРѕ какой компании РјС РјРѕР¶РµРј работать?",
effectiveAddressUserMessage: "РїРѕ какой компании РјС РјРѕР¶РµРј работать?",
followupContext: null,
llmPreDecomposeMeta: null,
useMock: false
});
expect(decision.runAddressLane).toBe(false);
expect(decision.toolGateReason).toBe("assistant_data_scope_query_detected");
expect(decision.livingMode).toBe("chat");
expect(decision.orchestrationContract?.hard_meta_mode).toBe("data_scope");
expect(decision.orchestrationContract?.provider_execution?.provider_mode).toBe("unknown");
});
it("keeps supported address intent in address lane", () => {
const policy = buildPolicy({
detectAddressQuestionMode: () => ({ mode: "address_query", confidence: "high" }),
resolveAddressIntent: () => ({ intent: "inventory_on_hand_as_of_date", confidence: "high" }),
resolveAddressToolGateDecision: () => ({
runAddressLane: true,
decision: "run_address_lane",
reason: "address_mode_classifier_detected"
})
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "какие товары сейчас лежат на складе",
effectiveAddressUserMessage: "какие товары сейчас лежат на складе",
followupContext: null,
llmPreDecomposeMeta: null,
useMock: false
});
expect(decision.runAddressLane).toBe(true);
expect(decision.toolGateReason).toBe("address_mode_classifier_detected");
expect(decision.livingMode).toBe("address_data");
expect(decision.orchestrationContract?.semantic_route_arbitration?.supported_address_intent_detected).toBe(true);
});
it("uses internal route-policy tool gate when no external override is provided", () => {
const policy = buildPolicy({
resolveAddressToolGateDecision: undefined,
detectAddressQuestionMode: () => ({ mode: "address_query", confidence: "high" }),
resolveAddressIntent: () => ({ intent: "inventory_on_hand_as_of_date", confidence: "high" }),
hasAddressLlmPreDecomposeCandidate: () => true
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "какие остатки на складе",
effectiveAddressUserMessage: "какие остатки на складе",
followupContext: null,
llmPreDecomposeMeta: null,
useMock: false
});
expect(decision.runAddressLane).toBe(true);
expect(decision.toolGateDecision).toBe("run_address_lane");
expect(decision.toolGateReason).toBe("address_mode_classifier_detected");
expect(decision.livingMode).toBe("address_data");
});
it("does not let deep session continuation override an exact VAT period route", () => {
const policy = buildPolicy({
detectAddressQuestionMode: () => ({ mode: "address_query", confidence: "high" }),
resolveAddressIntent: () => ({ intent: "vat_liability_confirmed_for_tax_period", confidence: "high" }),
resolveAddressToolGateDecision: () => ({
runAddressLane: true,
decision: "run_address_lane",
reason: "address_mode_classifier_detected"
}),
hasDeepSessionContinuationSignal: () => true
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "Р° какой НДС РјС РґРѕР»Р¶РЅС‹ сгрузить РЅР° март 2020?",
effectiveAddressUserMessage: "Р° какой НДС РјС РґРѕР»Р¶РЅС‹ сгрузить РЅР° март 2020?",
followupContext: null,
llmPreDecomposeMeta: {
applied: true,
llmCanonicalCandidateDetected: true,
predecomposeContract: {
mode: "address_query",
mode_confidence: "high",
intent: "vat_liability_confirmed_for_tax_period",
intent_confidence: "high"
},
semanticExtractionContract: {
valid: true,
apply_canonical_recommended: true,
extraction: {
query_shape: "UNKNOWN",
aggregation_profile: "unknown"
},
guard_hints: {
deep_investigation_signal_detected: false
}
}
} as any,
useMock: false
});
expect(decision.runAddressLane).toBe(true);
expect(decision.toolGateDecision).toBe("run_address_lane");
expect(decision.toolGateReason).toBe("address_mode_classifier_detected");
expect(decision.livingMode).toBe("address_data");
expect(decision.orchestrationContract?.deep_session_continuation_fallback_to_deep).toBe(false);
});
it("keeps an exact address route out of deep fallback when only semantic deep hint is present", () => {
const policy = buildPolicy({
detectAddressQuestionMode: () => ({ mode: "address_query", confidence: "high" }),
resolveAddressIntent: () => ({ intent: "vat_liability_confirmed_for_tax_period", confidence: "high" }),
resolveAddressToolGateDecision: () => ({
runAddressLane: true,
decision: "run_address_lane",
reason: "address_mode_classifier_detected"
})
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "какой НДС мы должны заплатить на март 2020",
effectiveAddressUserMessage: "какой НДС мы должны заплатить на март 2020",
followupContext: null,
llmPreDecomposeMeta: {
applied: true,
llmCanonicalCandidateDetected: true,
predecomposeContract: {
mode: "address_query",
mode_confidence: "high",
intent: "vat_liability_confirmed_for_tax_period",
intent_confidence: "high"
},
semanticExtractionContract: {
valid: true,
apply_canonical_recommended: true,
extraction: {
query_shape: "UNKNOWN",
aggregation_profile: "unknown"
},
guard_hints: {
deep_investigation_signal_detected: true
}
}
} as any,
useMock: false
});
expect(decision.runAddressLane).toBe(true);
expect(decision.livingMode).toBe("address_data");
expect(decision.orchestrationContract?.semantic_route_arbitration?.supported_address_intent_detected).toBe(true);
expect(decision.orchestrationContract?.deep_analysis_signal_fallback_to_deep).toBe(false);
});
it("routes memory recap follow-up over grounded answer to chat", () => {
const policy = buildPolicy({
resolveRouteMemorySignals: () => ({
contextualHistoricalCapabilityFollowupDetected: false,
contextualMemoryRecapFollowupDetected: true
}),
findLastGroundedAddressAnswerDebug: () => ({ execution_lane: "address_query" })
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "Р° СС РїРѕРјРЅРёС€СЊ что РјС РѕР±СЃСѓР¶РґР°Р»Рё?",
effectiveAddressUserMessage: "Р° СС РїРѕРјРЅРёС€СЊ что РјС РѕР±СЃСѓР¶РґР°Р»Рё?",
followupContext: null,
llmPreDecomposeMeta: {
applied: false,
reason: "normalized_fragment_rejected_semantic_guard",
predecomposeContract: {
mode: "unsupported",
mode_confidence: "low",
intent: "unknown",
intent_confidence: "low"
}
},
useMock: false
});
expect(decision.runAddressLane).toBe(false);
expect(decision.toolGateReason).toBe("memory_recap_followup_detected");
expect(decision.livingMode).toBe("chat");
expect(decision.livingReason).toBe("memory_recap_followup_detected");
});
it("routes answer inspection follow-up over grounded selected-object answer to chat", () => {
const policy = buildPolicy({
findLastGroundedAddressAnswerDebug: () => ({ execution_lane: "address_query" }),
resolveAddressToolGateDecision: () => ({
runAddressLane: true,
decision: "run_address_lane",
reason: "address_mode_classifier_detected"
}),
detectAddressQuestionMode: () => ({ mode: "address_query", confidence: "high" }),
isAnswerInspectionFollowupOverGroundedAnswer: () => true
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "у тебя написано кто контрагент: рабочая станция - это ошибка?",
effectiveAddressUserMessage: "у тебя написано кто контрагент: рабочая станция - это ошибка?",
followupContext: { previous_intent: "inventory_sale_trace_for_item", previous_anchor_type: "item" },
llmPreDecomposeMeta: null,
useMock: false
});
expect(decision.runAddressLane).toBe(false);
expect(decision.toolGateReason).toBe("answer_inspection_followup_over_grounded_answer");
expect(decision.livingMode).toBe("chat");
expect(decision.livingReason).toBe("answer_inspection_followup_detected");
});
it("routes organization fact lookup away from address lane even with follow-up context", () => {
const policy = buildPolicy({
hasDataRetrievalRequestSignal: () => true,
hasStrongDataIntentSignal: () => true,
detectAddressQuestionMode: () => ({ mode: "address_query", confidence: "high" }),
resolveAddressIntent: () => ({ intent: "inventory_on_hand_as_of_date", confidence: "low" }),
hasOrganizationFactLookupSignal: (text: unknown) =>
/возраст.*альтернатива плюс/i.test(String(text ?? "")),
resolveAddressToolGateDecision: () => ({
runAddressLane: true,
decision: "run_address_lane",
reason: "address_mode_classifier_detected"
})
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "а какой возраст у Альтернативы Плюс?",
effectiveAddressUserMessage: "Какой возраст у контрагента Альтернатива Плюс?",
followupContext: {
previous_intent: "inventory_on_hand_as_of_date",
previous_filters: {
organization: "ООО \"Альтернатива Плюс\"",
as_of_date: "2021-03-31"
},
previous_anchor_type: "item",
previous_anchor_value: "Модуль прямоугольный 1400*110*750"
},
llmPreDecomposeMeta: null,
useMock: false
});
expect(decision.runAddressLane).toBe(false);
expect(decision.toolGateReason).toBe("organization_fact_lookup_signal_detected");
expect(decision.livingMode).toBe("chat");
expect(decision.livingReason).toBe("organization_fact_lookup_signal_detected");
});
it("routes a late company switch to chat instead of reusing the old address contour", () => {
const policy = buildPolicy({
detectAddressQuestionMode: () => ({ mode: "address_query", confidence: "high" }),
resolveAddressIntent: () => ({ intent: "inventory_on_hand_as_of_date", confidence: "high" }),
resolveAddressToolGateDecision: () => ({
runAddressLane: true,
decision: "run_address_lane",
reason: "address_mode_classifier_detected"
}),
resolveOrganizationSelectionFromMessage: () => "РАЙМ",
shouldEmitOrganizationSelectionReply: () => true
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "теперь давай по РАЙМ",
effectiveAddressUserMessage: "теперь давай по РАЙМ",
followupContext: {
previous_intent: "inventory_on_hand_as_of_date",
previous_filters: {
organization: "ООО Альтернатива Плюс",
as_of_date: "2026-04-19"
}
},
sessionItems: [
{
role: "assistant",
debug: {
execution_lane: "address_query",
answer_grounding_check: { status: "grounded" },
extracted_filters: {
organization: "ООО Альтернатива Плюс",
as_of_date: "2026-04-19"
}
}
}
],
sessionOrganizationScope: {
knownOrganizations: ["ООО Альтернатива Плюс", "РАЙМ"],
selectedOrganization: null,
activeOrganization: "ООО Альтернатива Плюс"
},
llmPreDecomposeMeta: null,
useMock: false
});
expect(decision.runAddressLane).toBe(false);
expect(decision.toolGateReason).toBe("organization_scope_switch_detected");
expect(decision.livingMode).toBe("chat");
expect(decision.livingReason).toBe("organization_scope_switch_detected");
expect(decision.orchestrationContract?.organization_scope_switch_detected).toBe(true);
expect(decision.orchestrationContract?.organization_scope_selection).toBe("РАЙМ");
});
it("routes explicit recap wording with selected-object phrasing to chat even when address-like cues exist", () => {
const policy = buildPolicy({
hasStrongDataIntentSignal: () => true,
hasDataRetrievalRequestSignal: () => true,
resolveRouteMemorySignals: () => ({
contextualHistoricalCapabilityFollowupDetected: false,
contextualMemoryRecapFollowupDetected: true
}),
findLastGroundedAddressAnswerDebug: () => ({
execution_lane: "address_query",
detected_intent: "inventory_purchase_provenance_for_item"
})
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "а ты помнишь, что мы по этой позиции уже выяснили?",
effectiveAddressUserMessage: "а ты помнишь, что мы по этой позиции уже выяснили?",
followupContext: null,
llmPreDecomposeMeta: {
applied: false,
reason: "normalized_fragment_rejected_semantic_guard",
predecomposeContract: {
mode: "unsupported",
mode_confidence: "low",
intent: "unknown",
intent_confidence: "low"
}
},
useMock: false
});
expect(decision.runAddressLane).toBe(false);
expect(decision.toolGateReason).toBe("memory_recap_followup_detected");
expect(decision.livingMode).toBe("chat");
expect(decision.livingReason).toBe("memory_recap_followup_detected");
});
it("does not force unsupported-intent fallback when predecompose runtime is unavailable", () => {
const policy = buildPolicy({
hasStrongDataIntentSignal: () => true,
hasDataRetrievalRequestSignal: () => true,
resolveAddressToolGateDecision: () => ({
runAddressLane: true,
decision: "run_address_lane",
reason: "address_mode_classifier_detected"
})
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "покажи документы по сверке",
effectiveAddressUserMessage: "покажи документы по сверке",
followupContext: { root_context_only: true },
llmPreDecomposeMeta: {
reason: "error:OpenAI API key is missing",
predecomposeContract: {
mode: "unsupported",
mode_confidence: "low",
intent: "unknown",
intent_confidence: "low"
}
},
useMock: false
});
expect(decision.toolGateReason).toBe("address_mode_classifier_detected");
expect(decision.orchestrationContract?.unsupported_address_intent_fallback_to_deep).toBe(false);
expect(decision.orchestrationContract?.provider_execution?.llm_runtime_unavailable_detected).toBe(true);
});
it("does not classify colloquial VAT root query as non-domain when L0 address gate is positive", () => {
const policy = buildPolicy({
hasStrongDataIntentSignal: () => true,
resolveAddressToolGateDecision: () => ({
runAddressLane: true,
decision: "run_address_lane",
reason: "address_signal_detected"
})
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "скок ндс надо заплатить в налоговую на февраль 2017",
effectiveAddressUserMessage: "скок ндс надо заплатить в налоговую на февраль 2017",
followupContext: null,
llmPreDecomposeMeta: null,
useMock: true
});
expect(decision.runAddressLane).toBe(true);
expect(decision.toolGateReason).toBe("address_signal_detected");
expect(decision.livingMode).toBe("address_data");
});
it("does not mark organization selection after grounded continuity as non-domain noise", () => {
const policy = buildPolicy({
findLastGroundedAddressAnswerDebug: () => null,
findLastOrganizationClarificationAddressDebug: () => ({
organization_candidates: ["Org A", "Org B"]
}),
resolveOrganizationSelectionFromMessage: (userMessage: unknown, knownOrganizations: unknown) => {
const normalized = String(userMessage ?? "").trim().toLowerCase();
const candidates = Array.isArray(knownOrganizations) ? knownOrganizations.map((item) => String(item)) : [];
return candidates.find((candidate) => candidate.toLowerCase() === normalized) ?? null;
}
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "Org A",
effectiveAddressUserMessage: "Org A",
followupContext: null,
llmPreDecomposeMeta: null,
sessionItems: [
{
role: "assistant",
debug: {
execution_lane: "address_query",
answer_grounding_check: { status: "grounded" },
extracted_filters: {
organization: "Org A",
as_of_date: "2021-03-31"
},
detected_intent: "receivables_confirmed_as_of_date"
}
}
],
useMock: false
});
expect(decision.runAddressLane).toBe(false);
expect(decision.toolGateReason).toBe("no_address_signal_after_l0");
expect(decision.livingMode).toBe("chat");
expect(decision.orchestrationContract?.hard_meta_mode).toBeNull();
expect(decision.orchestrationContract?.followup_context_detected).toBe(false);
});
it("does not turn short entity follow-up into organization switch just because scope already has an active company", () => {
const policy = buildPolicy({
resolveAddressToolGateDecision: undefined,
findLastOrganizationClarificationAddressDebug: () => ({
execution_lane: "address_query",
limited_reason_category: "missing_anchor",
organization_candidates: ["ООО Альтернатива Плюс", "ООО Лайсвуд", "РАЙМ"]
}),
shouldEmitOrganizationSelectionReply: () => true
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "а по свк",
effectiveAddressUserMessage: "а по свк",
followupContext: {
previous_intent: "list_documents_by_counterparty",
previous_filters: {
counterparty: "Чепурнов П.Д."
},
previous_anchor_type: "counterparty",
previous_anchor_value: "Чепурнов П.Д."
},
sessionItems: [
{
role: "assistant",
debug: {
execution_lane: "address_query",
answer_grounding_check: { status: "grounded" },
detected_intent: "list_documents_by_counterparty",
extracted_filters: {
counterparty: "Чепурнов П.Д.",
organization: "ООО Альтернатива Плюс"
}
}
}
],
sessionOrganizationScope: {
knownOrganizations: ["ООО Альтернатива Плюс", "ООО Лайсвуд", "РАЙМ"],
selectedOrganization: "ООО Альтернатива Плюс",
activeOrganization: "ООО Альтернатива Плюс"
},
llmPreDecomposeMeta: null,
useMock: false
});
expect(decision.toolGateReason).not.toBe("organization_scope_switch_detected");
expect(decision.orchestrationContract?.organization_scope_switch_detected).not.toBe(true);
});
it("routes broad business evaluation follow-up to chat instead of replaying lifecycle address intent", () => {
const policy = buildPolicy({
resolveAddressIntent: () => ({ intent: "counterparty_activity_lifecycle", confidence: "high" }),
findLastGroundedAddressAnswerDebug: () => ({
execution_lane: "address_query",
answer_grounding_check: { status: "grounded" },
detected_intent: "counterparty_activity_lifecycle",
extracted_filters: {
organization: 'ООО "Альтернатива Плюс"',
period_to: "2026-04-18"
}
}),
resolveAssistantTurnMeaning: () => ({
schema_version: "assistant_turn_meaning_v1",
asked_domain_family: "business_summary",
asked_action_family: "broad_evaluation",
explicit_intent_candidate: null,
unsupported_but_understood_family: "broad_business_evaluation",
stale_replay_forbidden: true,
reason_codes: ["broad_business_evaluation_current_turn_signal"]
}),
resolveAddressToolGateDecision: () => ({
runAddressLane: false,
decision: "skip_address_lane",
reason: "no_address_signal_after_l0"
})
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage: "Как ты оценишь деятельность компании?",
effectiveAddressUserMessage: "Как ты оценишь деятельность компании?",
followupContext: null,
llmPreDecomposeMeta: {
applied: true,
predecomposeContract: {
mode: "unsupported",
mode_confidence: "low",
intent: "unknown",
intent_confidence: "low"
},
semanticExtractionContract: {
valid: true,
apply_canonical_recommended: true,
extraction: {
query_shape: "UNKNOWN",
aggregation_profile: "unknown"
},
guard_hints: {
deep_investigation_signal_detected: false
}
}
},
sessionItems: [
{
role: "assistant",
debug: {
execution_lane: "address_query",
answer_grounding_check: { status: "grounded" },
detected_intent: "counterparty_activity_lifecycle",
extracted_filters: {
organization: 'ООО "Альтернатива Плюс"',
period_to: "2026-04-18"
}
}
}
],
useMock: false
});
expect(decision.runAddressLane).toBe(false);
expect(decision.toolGateDecision).toBe("skip_address_lane");
expect(decision.toolGateReason).toBe("unsupported_current_turn_meaning_boundary");
expect(decision.livingMode).toBe("chat");
expect(decision.livingReason).toBe("unsupported_current_turn_meaning_boundary");
expect(decision.orchestrationContract?.unsupported_current_turn_family).toBe("broad_business_evaluation");
});
it("recovers an address route from current-turn meaning when L0 resolver is noisy", () => {
const policy = buildPolicy({
resolveAddressToolGateDecision: undefined,
resolveAssistantTurnMeaning: () => ({
schema_version: "assistant_turn_meaning_v1",
explicit_intent_candidate: "receivables_confirmed_as_of_date",
meaning_confidence: "high",
reason_codes: ["receivables_current_turn_meaning_signal"],
stale_replay_forbidden: false
})
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage:
"\u043a\u0442\u043e \u043d\u0430\u043c\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0434\u0435\u043d\u0435\u0433 \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f",
effectiveAddressUserMessage:
"\u043a\u0442\u043e \u043d\u0430\u043c\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0434\u0435\u043d\u0435\u0433 \u043d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f",
followupContext: null,
llmPreDecomposeMeta: null,
useMock: false
});
expect(decision.runAddressLane).toBe(true);
expect(decision.livingMode).toBe("address_data");
expect(decision.orchestrationContract?.address_intent).toBe("receivables_confirmed_as_of_date");
expect(decision.orchestrationContract?.assistant_turn_meaning?.schema_version).toBe(
"assistant_turn_meaning_v1"
);
});
it("routes unsupported-but-understood current meaning to boundary instead of stale address carryover", () => {
const policy = buildPolicy({
hasDataRetrievalRequestSignal: () => true,
hasStrongDataIntentSignal: () => true,
resolveAddressToolGateDecision: () => ({
runAddressLane: true,
decision: "run_address_lane",
reason: "followup_context_detected"
}),
resolveAssistantTurnMeaning: () => ({
schema_version: "assistant_turn_meaning_v1",
asked_domain_family: "counterparty",
asked_action_family: "counterparty_value_or_turnover",
explicit_intent_candidate: null,
unsupported_but_understood_family: "counterparty_value_or_turnover",
stale_replay_forbidden: true,
reason_codes: ["counterparty_turnover_current_turn_signal"]
})
});
const decision = policy.resolveAssistantOrchestrationDecision({
rawUserMessage:
"\u043a\u0430\u043a\u043e\u0439 \u043e\u0431\u043e\u0440\u043e\u0442 \u0431\u044b\u043b \u0441\u0432\u043a",
effectiveAddressUserMessage:
"\u043a\u0430\u043a\u043e\u0439 \u043e\u0431\u043e\u0440\u043e\u0442 \u0431\u044b\u043b \u0441\u0432\u043a",
followupContext: {
previous_intent: "list_documents_by_counterparty",
previous_filters: {
counterparty: "Previous Counterparty"
}
},
llmPreDecomposeMeta: null,
useMock: false
});
expect(decision.runAddressLane).toBe(false);
expect(decision.toolGateReason).toBe("unsupported_current_turn_meaning_boundary");
expect(decision.livingMode).toBe("chat");
expect(decision.orchestrationContract?.unsupported_current_turn_meaning_boundary).toBe(true);
expect(decision.orchestrationContract?.unsupported_current_turn_family).toBe(
"counterparty_value_or_turnover"
);
});
});