Архитектура: централизовать selected-item carryover в continuity policy и transition glue
This commit is contained in:
@@ -16,6 +16,7 @@ exports.applyTemporalCarryoverFilters = applyTemporalCarryoverFilters;
|
||||
exports.applyOrganizationCarryoverFilters = applyOrganizationCarryoverFilters;
|
||||
exports.applyHistoricalPartyCarryoverFilters = applyHistoricalPartyCarryoverFilters;
|
||||
exports.applyReferencedEntityCarryover = applyReferencedEntityCarryover;
|
||||
exports.applySelectedItemCarryover = applySelectedItemCarryover;
|
||||
exports.buildInventoryRootFrameFromAddressDebug = buildInventoryRootFrameFromAddressDebug;
|
||||
exports.isGroundedAddressDebug = isGroundedAddressDebug;
|
||||
exports.resolveAssistantContinuitySnapshot = resolveAssistantContinuitySnapshot;
|
||||
@@ -352,6 +353,33 @@ function applyReferencedEntityCarryover(previousFilters, previousAnchorType, pre
|
||||
resolvedCounterpartyFromDisplay
|
||||
};
|
||||
}
|
||||
function applySelectedItemCarryover(previousFilters, previousAnchorType, previousAnchorValue, rootScopedPivot, shouldApplySelectedItemCarryover, navigationFocusItemLabel, continuityActiveItem, selectedObjectLabelFromUser, selectedObjectLabelFromAlternate, toNonEmptyString = fallbackToNonEmptyString) {
|
||||
const nextFilters = previousFilters && typeof previousFilters === "object" ? { ...previousFilters } : {};
|
||||
if (rootScopedPivot || !shouldApplySelectedItemCarryover || toNonEmptyString(nextFilters.item)) {
|
||||
return {
|
||||
previousFilters: nextFilters,
|
||||
previousAnchorType,
|
||||
previousAnchorValue
|
||||
};
|
||||
}
|
||||
const selectedObjectLabel = toNonEmptyString(navigationFocusItemLabel) ??
|
||||
toNonEmptyString(continuityActiveItem) ??
|
||||
toNonEmptyString(selectedObjectLabelFromUser) ??
|
||||
toNonEmptyString(selectedObjectLabelFromAlternate);
|
||||
if (!selectedObjectLabel) {
|
||||
return {
|
||||
previousFilters: nextFilters,
|
||||
previousAnchorType,
|
||||
previousAnchorValue
|
||||
};
|
||||
}
|
||||
nextFilters.item = selectedObjectLabel;
|
||||
return {
|
||||
previousFilters: nextFilters,
|
||||
previousAnchorType: "item",
|
||||
previousAnchorValue: selectedObjectLabel
|
||||
};
|
||||
}
|
||||
function buildInventoryRootFrameFromAddressDebug(debug, toNonEmptyString = fallbackToNonEmptyString) {
|
||||
if (!debug || typeof debug !== "object") {
|
||||
return null;
|
||||
|
||||
@@ -667,27 +667,19 @@ function createAssistantTransitionPolicy(deps) {
|
||||
followupSelectionMode,
|
||||
resolvedCounterpartyFromDisplay
|
||||
} = (0, assistantContinuityPolicy_1.applyReferencedEntityCarryover)(previousFilters, previousAnchorType, previousAnchor, followupSelectionMode, resolvedEntityFromFollowup, rootScopedPivot, deps.toNonEmptyString));
|
||||
if (!rootScopedPivot &&
|
||||
!deps.toNonEmptyString(previousFilters.item) &&
|
||||
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
||||
sourceIntentHint === "inventory_purchase_provenance_for_item" ||
|
||||
sourceIntentHint === "inventory_purchase_documents_for_item" ||
|
||||
sourceIntentHint === "inventory_sale_trace_for_item" ||
|
||||
sourceIntentHint === "inventory_profitability_for_item" ||
|
||||
sourceIntentHint === "inventory_purchase_to_sale_chain" ||
|
||||
sourceIntentHint === "inventory_aging_by_purchase_date" ||
|
||||
hasSelectedObjectInventorySignalPrimary ||
|
||||
hasSelectedObjectInventorySignalAlternate)) {
|
||||
const selectedObjectLabel = (navigationFocusObjectType === "item" ? navigationFocusObjectLabel : null) ??
|
||||
continuitySnapshot.activeItem ??
|
||||
extractSelectedObjectLabel(userMessage) ??
|
||||
(deps.toNonEmptyString(alternateMessage) ? extractSelectedObjectLabel(String(alternateMessage ?? "")) : null);
|
||||
if (selectedObjectLabel) {
|
||||
previousFilters.item = selectedObjectLabel;
|
||||
previousAnchorType = "item";
|
||||
previousAnchor = selectedObjectLabel;
|
||||
}
|
||||
}
|
||||
({
|
||||
previousFilters,
|
||||
previousAnchorType,
|
||||
previousAnchorValue: previousAnchor
|
||||
} = (0, assistantContinuityPolicy_1.applySelectedItemCarryover)(previousFilters, previousAnchorType, previousAnchor, rootScopedPivot, sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
||||
sourceIntentHint === "inventory_purchase_provenance_for_item" ||
|
||||
sourceIntentHint === "inventory_purchase_documents_for_item" ||
|
||||
sourceIntentHint === "inventory_sale_trace_for_item" ||
|
||||
sourceIntentHint === "inventory_profitability_for_item" ||
|
||||
sourceIntentHint === "inventory_purchase_to_sale_chain" ||
|
||||
sourceIntentHint === "inventory_aging_by_purchase_date" ||
|
||||
hasSelectedObjectInventorySignalPrimary ||
|
||||
hasSelectedObjectInventorySignalAlternate, navigationFocusObjectType === "item" ? navigationFocusObjectLabel : null, continuitySnapshot.activeItem, extractSelectedObjectLabel(userMessage), deps.toNonEmptyString(alternateMessage) ? extractSelectedObjectLabel(String(alternateMessage ?? "")) : null, deps.toNonEmptyString));
|
||||
if (explicitOrganizationClarificationSelection && !previousAnchor) {
|
||||
previousAnchorType = "organization";
|
||||
previousAnchor = explicitOrganizationClarificationSelection;
|
||||
|
||||
@@ -536,6 +536,53 @@ export function applyReferencedEntityCarryover(
|
||||
};
|
||||
}
|
||||
|
||||
export function applySelectedItemCarryover(
|
||||
previousFilters: Record<string, unknown> | null,
|
||||
previousAnchorType: string | null,
|
||||
previousAnchorValue: string | null,
|
||||
rootScopedPivot: boolean,
|
||||
shouldApplySelectedItemCarryover: boolean,
|
||||
navigationFocusItemLabel: unknown,
|
||||
continuityActiveItem: unknown,
|
||||
selectedObjectLabelFromUser: unknown,
|
||||
selectedObjectLabelFromAlternate: unknown,
|
||||
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
|
||||
): {
|
||||
previousFilters: Record<string, unknown>;
|
||||
previousAnchorType: string | null;
|
||||
previousAnchorValue: string | null;
|
||||
} {
|
||||
const nextFilters =
|
||||
previousFilters && typeof previousFilters === "object" ? { ...previousFilters } : {};
|
||||
if (rootScopedPivot || !shouldApplySelectedItemCarryover || toNonEmptyString(nextFilters.item)) {
|
||||
return {
|
||||
previousFilters: nextFilters,
|
||||
previousAnchorType,
|
||||
previousAnchorValue
|
||||
};
|
||||
}
|
||||
|
||||
const selectedObjectLabel =
|
||||
toNonEmptyString(navigationFocusItemLabel) ??
|
||||
toNonEmptyString(continuityActiveItem) ??
|
||||
toNonEmptyString(selectedObjectLabelFromUser) ??
|
||||
toNonEmptyString(selectedObjectLabelFromAlternate);
|
||||
if (!selectedObjectLabel) {
|
||||
return {
|
||||
previousFilters: nextFilters,
|
||||
previousAnchorType,
|
||||
previousAnchorValue
|
||||
};
|
||||
}
|
||||
|
||||
nextFilters.item = selectedObjectLabel;
|
||||
return {
|
||||
previousFilters: nextFilters,
|
||||
previousAnchorType: "item",
|
||||
previousAnchorValue: selectedObjectLabel
|
||||
};
|
||||
}
|
||||
|
||||
export function buildInventoryRootFrameFromAddressDebug(
|
||||
debug: Record<string, unknown> | null,
|
||||
toNonEmptyString: (value: unknown) => string | null = fallbackToNonEmptyString
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
applyHistoricalPartyCarryoverFilters,
|
||||
applyOrganizationCarryoverFilters,
|
||||
applyReferencedEntityCarryover,
|
||||
applySelectedItemCarryover,
|
||||
applyTemporalCarryoverFilters,
|
||||
buildRootScopedCarryoverFilters,
|
||||
buildInventoryRootFrameFromAddressDebug,
|
||||
@@ -874,10 +875,16 @@ export function createAssistantTransitionPolicy(deps) {
|
||||
rootScopedPivot,
|
||||
deps.toNonEmptyString
|
||||
));
|
||||
if (
|
||||
!rootScopedPivot &&
|
||||
!deps.toNonEmptyString(previousFilters.item) &&
|
||||
(sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
||||
({
|
||||
previousFilters,
|
||||
previousAnchorType,
|
||||
previousAnchorValue: previousAnchor
|
||||
} = applySelectedItemCarryover(
|
||||
previousFilters,
|
||||
previousAnchorType,
|
||||
previousAnchor,
|
||||
rootScopedPivot,
|
||||
sourceIntentHint === "inventory_on_hand_as_of_date" ||
|
||||
sourceIntentHint === "inventory_purchase_provenance_for_item" ||
|
||||
sourceIntentHint === "inventory_purchase_documents_for_item" ||
|
||||
sourceIntentHint === "inventory_sale_trace_for_item" ||
|
||||
@@ -885,19 +892,13 @@ export function createAssistantTransitionPolicy(deps) {
|
||||
sourceIntentHint === "inventory_purchase_to_sale_chain" ||
|
||||
sourceIntentHint === "inventory_aging_by_purchase_date" ||
|
||||
hasSelectedObjectInventorySignalPrimary ||
|
||||
hasSelectedObjectInventorySignalAlternate)
|
||||
) {
|
||||
const selectedObjectLabel =
|
||||
(navigationFocusObjectType === "item" ? navigationFocusObjectLabel : null) ??
|
||||
continuitySnapshot.activeItem ??
|
||||
extractSelectedObjectLabel(userMessage) ??
|
||||
(deps.toNonEmptyString(alternateMessage) ? extractSelectedObjectLabel(String(alternateMessage ?? "")) : null);
|
||||
if (selectedObjectLabel) {
|
||||
previousFilters.item = selectedObjectLabel;
|
||||
previousAnchorType = "item";
|
||||
previousAnchor = selectedObjectLabel;
|
||||
}
|
||||
}
|
||||
hasSelectedObjectInventorySignalAlternate,
|
||||
navigationFocusObjectType === "item" ? navigationFocusObjectLabel : null,
|
||||
continuitySnapshot.activeItem,
|
||||
extractSelectedObjectLabel(userMessage),
|
||||
deps.toNonEmptyString(alternateMessage) ? extractSelectedObjectLabel(String(alternateMessage ?? "")) : null,
|
||||
deps.toNonEmptyString
|
||||
));
|
||||
if (explicitOrganizationClarificationSelection && !previousAnchor) {
|
||||
previousAnchorType = "organization";
|
||||
previousAnchor = explicitOrganizationClarificationSelection;
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
applyHistoricalPartyCarryoverFilters,
|
||||
applyOrganizationCarryoverFilters,
|
||||
applyReferencedEntityCarryover,
|
||||
applySelectedItemCarryover,
|
||||
applyTemporalCarryoverFilters,
|
||||
buildRootScopedCarryoverFilters,
|
||||
hydrateInventoryRootFrameState,
|
||||
@@ -324,4 +325,29 @@ describe("assistantContinuityPolicy organization authority", () => {
|
||||
resolvedCounterpartyFromDisplay: true
|
||||
});
|
||||
});
|
||||
|
||||
it("applies selected-item carryover from navigation focus before continuity and explicit labels", () => {
|
||||
const carryover = applySelectedItemCarryover(
|
||||
{
|
||||
organization: "Org Alt"
|
||||
},
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
true,
|
||||
"Workstation Navigation",
|
||||
"Workstation Continuity",
|
||||
"Workstation User",
|
||||
"Workstation Alternate"
|
||||
);
|
||||
|
||||
expect(carryover).toEqual({
|
||||
previousFilters: {
|
||||
organization: "Org Alt",
|
||||
item: "Workstation Navigation"
|
||||
},
|
||||
previousAnchorType: "item",
|
||||
previousAnchorValue: "Workstation Navigation"
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -297,6 +297,48 @@ describe("assistantTransitionPolicy", () => {
|
||||
expect(carryover?.followupContext?.root_context_only).toBeUndefined();
|
||||
});
|
||||
|
||||
it("hydrates selected-item carryover through shared continuity helper for short object follow-up", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
text: "Подтвержден складской срез по выбранной позиции.",
|
||||
debug: {
|
||||
detected_intent: "inventory_on_hand_as_of_date",
|
||||
extracted_filters: {
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
as_of_date: "2020-03-31"
|
||||
}
|
||||
}
|
||||
}),
|
||||
hasAddressFollowupContextSignal: () => true,
|
||||
hasShortInventoryObjectFollowupSignal: () => true,
|
||||
findRecentInventoryRootFrame: () => null,
|
||||
resolveAddressIntent: () => ({ intent: "unknown" })
|
||||
});
|
||||
|
||||
const carryover = policy.resolveAddressFollowupCarryoverContext(
|
||||
"по этой позиции покажи документы",
|
||||
[],
|
||||
null,
|
||||
null,
|
||||
{
|
||||
session_context: {
|
||||
active_focus_object: {
|
||||
object_type: "item",
|
||||
label: "Workstation Focus"
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
expect(carryover?.followupContext?.previous_filters).toMatchObject({
|
||||
organization: 'ООО "Альтернатива Плюс"',
|
||||
as_of_date: "2020-03-31",
|
||||
item: "Workstation Focus"
|
||||
});
|
||||
expect(carryover?.followupContext?.previous_anchor_type).toBe("item");
|
||||
expect(carryover?.followupContext?.previous_anchor_value).toBe("Workstation Focus");
|
||||
});
|
||||
|
||||
it("hydrates follow-up organization from shared assistant authority when local history filters are empty", () => {
|
||||
const policy = buildPolicy({
|
||||
findLastAddressAssistantItem: () => ({
|
||||
|
||||
Reference in New Issue
Block a user