АРЧ АП11 - Архитектура после регресса: Архитектура: централизовать organization authority в continuity policy для route, living-chat и data-scope
This commit is contained in:
@@ -8,6 +8,8 @@ exports.readAddressDebugScopedDate = readAddressDebugScopedDate;
|
||||
exports.buildInventoryRootFrameFromAddressDebug = buildInventoryRootFrameFromAddressDebug;
|
||||
exports.isGroundedAddressDebug = isGroundedAddressDebug;
|
||||
exports.resolveAssistantContinuitySnapshot = resolveAssistantContinuitySnapshot;
|
||||
exports.resolveAssistantOrganizationAuthority = resolveAssistantOrganizationAuthority;
|
||||
const assistantOrganizationMatcher_1 = require("./assistantOrganizationMatcher");
|
||||
function fallbackToNonEmptyString(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
@@ -21,6 +23,12 @@ function toRecordObject(value) {
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function mergeKnownOrganizationsDefault(values) {
|
||||
return (0, assistantOrganizationMatcher_1.mergeKnownOrganizations)(values);
|
||||
}
|
||||
function normalizeOrganizationScopeDefault(value) {
|
||||
return (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)(value);
|
||||
}
|
||||
function formatIsoDateForReply(value) {
|
||||
const source = String(value ?? "").trim();
|
||||
const match = source.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
||||
@@ -166,3 +174,87 @@ function resolveAssistantContinuitySnapshot(input) {
|
||||
hasGroundedInventoryContext: Boolean(lastGroundedInventoryAddressDebug)
|
||||
};
|
||||
}
|
||||
function collectAssistantOrganizationSignals(sessionItems, normalizeOrganizationScopeValue) {
|
||||
let lastAssistantSelectedOrganization = null;
|
||||
let lastAssistantActiveOrganization = null;
|
||||
const knownOrganizationsFromAssistantHistory = [];
|
||||
for (let index = sessionItems.length - 1; index >= 0; index -= 1) {
|
||||
const item = sessionItems[index];
|
||||
if (!item || item.role !== "assistant" || !item.debug || typeof item.debug !== "object") {
|
||||
continue;
|
||||
}
|
||||
const debug = item.debug;
|
||||
if (!lastAssistantActiveOrganization) {
|
||||
lastAssistantActiveOrganization = normalizeOrganizationScopeValue(debug.assistant_active_organization);
|
||||
}
|
||||
if (!lastAssistantSelectedOrganization) {
|
||||
lastAssistantSelectedOrganization = normalizeOrganizationScopeValue(debug.living_chat_selected_organization);
|
||||
}
|
||||
const directKnownOrganizations = Array.isArray(debug.assistant_known_organizations)
|
||||
? debug.assistant_known_organizations
|
||||
: [];
|
||||
const probeOrganizations = Array.isArray(debug.living_chat_data_scope_probe_organizations)
|
||||
? debug.living_chat_data_scope_probe_organizations
|
||||
: [];
|
||||
const organizationCandidates = Array.isArray(debug.organization_candidates) ? debug.organization_candidates : [];
|
||||
const directResolvedOrganizations = [
|
||||
debug.assistant_active_organization,
|
||||
debug.living_chat_selected_organization,
|
||||
readAddressDebugOrganization(debug, normalizeOrganizationScopeValue)
|
||||
];
|
||||
knownOrganizationsFromAssistantHistory.push(...directKnownOrganizations, ...probeOrganizations, ...organizationCandidates, ...directResolvedOrganizations);
|
||||
}
|
||||
return {
|
||||
lastAssistantSelectedOrganization,
|
||||
lastAssistantActiveOrganization,
|
||||
knownOrganizationsFromAssistantHistory: mergeKnownOrganizationsDefault(knownOrganizationsFromAssistantHistory)
|
||||
};
|
||||
}
|
||||
function resolveAssistantOrganizationAuthority(input) {
|
||||
const normalizeOrganizationScopeValue = input.normalizeOrganizationScopeValue ?? normalizeOrganizationScopeDefault;
|
||||
const mergeKnownOrganizations = input.mergeKnownOrganizations ?? mergeKnownOrganizationsDefault;
|
||||
const sessionItems = Array.isArray(input.sessionItems) ? input.sessionItems : [];
|
||||
const continuitySnapshot = resolveAssistantContinuitySnapshot({
|
||||
sessionItems,
|
||||
toNonEmptyString: input.toNonEmptyString ?? normalizeOrganizationScopeValue
|
||||
});
|
||||
const assistantSignals = collectAssistantOrganizationSignals(sessionItems, normalizeOrganizationScopeValue);
|
||||
const continuityActiveOrganization = normalizeOrganizationScopeValue(input.sessionActiveOrganization) ??
|
||||
assistantSignals.lastAssistantActiveOrganization ??
|
||||
normalizeOrganizationScopeValue(continuitySnapshot.activeOrganization);
|
||||
const knownOrganizations = mergeKnownOrganizations([
|
||||
...(Array.isArray(input.sessionKnownOrganizations) ? input.sessionKnownOrganizations : []),
|
||||
...assistantSignals.knownOrganizationsFromAssistantHistory,
|
||||
input.sessionSelectedOrganization,
|
||||
input.sessionActiveOrganization,
|
||||
continuityActiveOrganization
|
||||
]);
|
||||
const selectedOrganization = normalizeOrganizationScopeValue(input.sessionSelectedOrganization) ??
|
||||
assistantSignals.lastAssistantSelectedOrganization;
|
||||
const activeOrganization = normalizeOrganizationScopeValue(input.sessionActiveOrganization) ??
|
||||
selectedOrganization ??
|
||||
assistantSignals.lastAssistantActiveOrganization ??
|
||||
continuityActiveOrganization ??
|
||||
(knownOrganizations.length === 1 ? knownOrganizations[0] : null);
|
||||
const organizationClarificationCandidates = Array.isArray(input.lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? mergeKnownOrganizations([
|
||||
...input.lastOrganizationClarificationDebug.organization_candidates,
|
||||
...knownOrganizations,
|
||||
selectedOrganization,
|
||||
activeOrganization,
|
||||
continuityActiveOrganization
|
||||
])
|
||||
: [];
|
||||
const organizationClarificationSelectionFromScope = selectedOrganization ?? activeOrganization;
|
||||
return {
|
||||
continuitySnapshot,
|
||||
continuityActiveOrganization,
|
||||
lastAssistantSelectedOrganization: assistantSignals.lastAssistantSelectedOrganization,
|
||||
lastAssistantActiveOrganization: assistantSignals.lastAssistantActiveOrganization,
|
||||
knownOrganizations,
|
||||
selectedOrganization,
|
||||
activeOrganization,
|
||||
organizationClarificationCandidates,
|
||||
organizationClarificationSelectionFromScope
|
||||
};
|
||||
}
|
||||
|
||||
@@ -219,36 +219,13 @@ function createAssistantDataScopePolicy(deps) {
|
||||
return (0, assistantOrganizationMatcher_1.mergeKnownOrganizations)(collected, 20);
|
||||
}
|
||||
function findLastAssistantActiveOrganization(items) {
|
||||
const continuitySnapshot = (0, assistantContinuityPolicy_1.resolveAssistantContinuitySnapshot)({
|
||||
const authority = (0, assistantContinuityPolicy_1.resolveAssistantOrganizationAuthority)({
|
||||
sessionItems: items,
|
||||
toNonEmptyString: assistantOrganizationMatcher_1.normalizeOrganizationScopeValue
|
||||
toNonEmptyString: assistantOrganizationMatcher_1.normalizeOrganizationScopeValue,
|
||||
normalizeOrganizationScopeValue: assistantOrganizationMatcher_1.normalizeOrganizationScopeValue,
|
||||
mergeKnownOrganizations: assistantOrganizationMatcher_1.mergeKnownOrganizations
|
||||
});
|
||||
const continuityOrganization = (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)(continuitySnapshot.activeOrganization);
|
||||
for (let index = (Array.isArray(items) ? items.length : 0) - 1; index >= 0; index -= 1) {
|
||||
const item = Array.isArray(items) ? items[index] : null;
|
||||
if (!item || typeof item !== "object" || item.role !== "assistant") {
|
||||
continue;
|
||||
}
|
||||
const debug = item.debug;
|
||||
if (!debug || typeof debug !== "object") {
|
||||
continue;
|
||||
}
|
||||
const direct = (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)(debug.assistant_active_organization);
|
||||
if (direct) {
|
||||
return direct;
|
||||
}
|
||||
const selected = (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)(debug.living_chat_selected_organization);
|
||||
if (selected) {
|
||||
return selected;
|
||||
}
|
||||
if ((0, assistantContinuityPolicy_1.isGroundedAddressDebug)(debug)) {
|
||||
const groundedOrganization = (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)((0, assistantContinuityPolicy_1.readAddressDebugOrganization)(debug));
|
||||
if (groundedOrganization) {
|
||||
return groundedOrganization;
|
||||
}
|
||||
}
|
||||
}
|
||||
return continuityOrganization;
|
||||
return (0, assistantOrganizationMatcher_1.normalizeOrganizationScopeValue)(authority.activeOrganization);
|
||||
}
|
||||
function extractOrganizationFactsFromRows(rows) {
|
||||
const names = [];
|
||||
|
||||
@@ -152,10 +152,18 @@ function buildAddressMemoryRecapReply(input) {
|
||||
}
|
||||
async function runAssistantLivingChatRuntime(input) {
|
||||
const userMessage = String(input.userMessage ?? "");
|
||||
const continuitySnapshot = (0, assistantContinuityPolicy_1.resolveAssistantContinuitySnapshot)({
|
||||
const organizationAuthority = (0, assistantContinuityPolicy_1.resolveAssistantOrganizationAuthority)({
|
||||
sessionItems: input.sessionItems,
|
||||
toNonEmptyString: input.toNonEmptyString
|
||||
sessionKnownOrganizations: Array.isArray(input.sessionScope.knownOrganizations)
|
||||
? input.sessionScope.knownOrganizations
|
||||
: [],
|
||||
sessionSelectedOrganization: input.sessionScope.selectedOrganization,
|
||||
sessionActiveOrganization: input.sessionScope.activeOrganization,
|
||||
toNonEmptyString: input.toNonEmptyString,
|
||||
normalizeOrganizationScopeValue: input.toNonEmptyString,
|
||||
mergeKnownOrganizations: input.mergeKnownOrganizations
|
||||
});
|
||||
const continuitySnapshot = organizationAuthority.continuitySnapshot;
|
||||
const dataScopeMetaQuery = input.hasAssistantDataScopeMetaQuestionSignal(userMessage);
|
||||
const capabilityMetaQuery = input.shouldHandleAsAssistantCapabilityMetaQuery(userMessage);
|
||||
const destructiveSignal = input.hasDestructiveDataActionSignal(userMessage);
|
||||
@@ -169,13 +177,10 @@ async function runAssistantLivingChatRuntime(input) {
|
||||
let livingChatGroundingGuardApplied = false;
|
||||
let livingChatGroundingGuardReason = null;
|
||||
let livingChatProactiveScopeOfferApplied = false;
|
||||
const continuityActiveOrganization = input.toNonEmptyString(continuitySnapshot.activeOrganization);
|
||||
let knownOrganizations = input.mergeKnownOrganizations([
|
||||
...(Array.isArray(input.sessionScope.knownOrganizations) ? input.sessionScope.knownOrganizations : []),
|
||||
continuityActiveOrganization
|
||||
]);
|
||||
let selectedOrganization = input.toNonEmptyString(input.sessionScope.selectedOrganization);
|
||||
let activeOrganization = input.toNonEmptyString(input.sessionScope.activeOrganization) ?? continuityActiveOrganization;
|
||||
const continuityActiveOrganization = organizationAuthority.continuityActiveOrganization;
|
||||
let knownOrganizations = [...organizationAuthority.knownOrganizations];
|
||||
let selectedOrganization = organizationAuthority.selectedOrganization;
|
||||
let activeOrganization = organizationAuthority.activeOrganization;
|
||||
const memoryRecapContext = (0, assistantMemoryRecapPolicy_1.resolveAssistantLivingChatMemoryContext)({
|
||||
modeDecisionReason: input.modeDecision?.reason ?? null,
|
||||
sessionItems: input.sessionItems
|
||||
|
||||
+14
-15
@@ -72,26 +72,25 @@ function createAssistantRoutePolicy(deps) {
|
||||
const sessionOrganizationScope = input?.sessionOrganizationScope && typeof input.sessionOrganizationScope === "object"
|
||||
? input.sessionOrganizationScope
|
||||
: null;
|
||||
const continuitySnapshot = (0, assistantContinuityPolicy_1.resolveAssistantContinuitySnapshot)({
|
||||
const organizationAuthority = (0, assistantContinuityPolicy_1.resolveAssistantOrganizationAuthority)({
|
||||
sessionItems,
|
||||
toNonEmptyString
|
||||
sessionKnownOrganizations: Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||
? sessionOrganizationScope.knownOrganizations
|
||||
: [],
|
||||
sessionSelectedOrganization: sessionOrganizationScope?.selectedOrganization,
|
||||
sessionActiveOrganization: sessionOrganizationScope?.activeOrganization,
|
||||
lastOrganizationClarificationDebug: findLastOrganizationClarificationAddressDebug(sessionItems),
|
||||
toNonEmptyString,
|
||||
normalizeOrganizationScopeValue,
|
||||
mergeKnownOrganizations
|
||||
});
|
||||
const continuityActiveOrganization = normalizeOrganizationScopeValue(sessionOrganizationScope?.activeOrganization) ??
|
||||
normalizeOrganizationScopeValue(continuitySnapshot.activeOrganization);
|
||||
const continuitySnapshot = organizationAuthority.continuitySnapshot;
|
||||
const continuityActiveOrganization = organizationAuthority.continuityActiveOrganization;
|
||||
const lastGroundedAddressDebug = findLastGroundedAddressAnswerDebug(sessionItems) ??
|
||||
continuitySnapshot.lastGroundedAddressDebug;
|
||||
const lastOrganizationClarificationDebug = findLastOrganizationClarificationAddressDebug(sessionItems);
|
||||
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? mergeKnownOrganizations([
|
||||
...lastOrganizationClarificationDebug.organization_candidates,
|
||||
...((Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||
? sessionOrganizationScope.knownOrganizations
|
||||
: [])),
|
||||
continuityActiveOrganization
|
||||
])
|
||||
: [];
|
||||
const organizationClarificationSelectionFromScope = normalizeOrganizationScopeValue(sessionOrganizationScope?.selectedOrganization) ??
|
||||
continuityActiveOrganization;
|
||||
const organizationClarificationCandidates = organizationAuthority.organizationClarificationCandidates;
|
||||
const organizationClarificationSelectionFromScope = organizationAuthority.organizationClarificationSelectionFromScope;
|
||||
const organizationClarificationSelection = resolveOrganizationSelectionFromMessage(rawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(repairedRawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(effectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
import {
|
||||
mergeKnownOrganizations as mergeKnownOrganizationsFromMatcher,
|
||||
normalizeOrganizationScopeValue as normalizeOrganizationScopeValueFromMatcher
|
||||
} from "./assistantOrganizationMatcher";
|
||||
|
||||
export interface AssistantContinuitySnapshotInput {
|
||||
sessionItems?: unknown[];
|
||||
toNonEmptyString?: (value: unknown) => string | null;
|
||||
@@ -22,6 +27,29 @@ export interface AssistantContinuitySnapshot {
|
||||
hasGroundedInventoryContext: boolean;
|
||||
}
|
||||
|
||||
export interface AssistantOrganizationAuthorityInput {
|
||||
sessionItems?: unknown[];
|
||||
sessionKnownOrganizations?: unknown[];
|
||||
sessionSelectedOrganization?: unknown;
|
||||
sessionActiveOrganization?: unknown;
|
||||
lastOrganizationClarificationDebug?: Record<string, unknown> | null;
|
||||
toNonEmptyString?: (value: unknown) => string | null;
|
||||
normalizeOrganizationScopeValue?: (value: unknown) => string | null;
|
||||
mergeKnownOrganizations?: (values: unknown[]) => string[];
|
||||
}
|
||||
|
||||
export interface AssistantOrganizationAuthority {
|
||||
continuitySnapshot: AssistantContinuitySnapshot;
|
||||
continuityActiveOrganization: string | null;
|
||||
lastAssistantSelectedOrganization: string | null;
|
||||
lastAssistantActiveOrganization: string | null;
|
||||
knownOrganizations: string[];
|
||||
selectedOrganization: string | null;
|
||||
activeOrganization: string | null;
|
||||
organizationClarificationCandidates: string[];
|
||||
organizationClarificationSelectionFromScope: string | null;
|
||||
}
|
||||
|
||||
function fallbackToNonEmptyString(value: unknown): string | null {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
@@ -37,6 +65,14 @@ function toRecordObject(value: unknown): Record<string, unknown> | null {
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function mergeKnownOrganizationsDefault(values: unknown[]): string[] {
|
||||
return mergeKnownOrganizationsFromMatcher(values);
|
||||
}
|
||||
|
||||
function normalizeOrganizationScopeDefault(value: unknown): string | null {
|
||||
return normalizeOrganizationScopeValueFromMatcher(value);
|
||||
}
|
||||
|
||||
export function formatIsoDateForReply(value: unknown): string | null {
|
||||
const source = String(value ?? "").trim();
|
||||
const match = source.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
||||
@@ -228,3 +264,113 @@ export function resolveAssistantContinuitySnapshot(
|
||||
hasGroundedInventoryContext: Boolean(lastGroundedInventoryAddressDebug)
|
||||
};
|
||||
}
|
||||
|
||||
function collectAssistantOrganizationSignals(
|
||||
sessionItems: unknown[],
|
||||
normalizeOrganizationScopeValue: (value: unknown) => string | null
|
||||
): {
|
||||
lastAssistantSelectedOrganization: string | null;
|
||||
lastAssistantActiveOrganization: string | null;
|
||||
knownOrganizationsFromAssistantHistory: string[];
|
||||
} {
|
||||
let lastAssistantSelectedOrganization: string | null = null;
|
||||
let lastAssistantActiveOrganization: string | null = null;
|
||||
const knownOrganizationsFromAssistantHistory: unknown[] = [];
|
||||
|
||||
for (let index = sessionItems.length - 1; index >= 0; index -= 1) {
|
||||
const item = sessionItems[index] as { role?: string; debug?: Record<string, unknown> } | null;
|
||||
if (!item || item.role !== "assistant" || !item.debug || typeof item.debug !== "object") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const debug = item.debug;
|
||||
if (!lastAssistantActiveOrganization) {
|
||||
lastAssistantActiveOrganization = normalizeOrganizationScopeValue(debug.assistant_active_organization);
|
||||
}
|
||||
if (!lastAssistantSelectedOrganization) {
|
||||
lastAssistantSelectedOrganization = normalizeOrganizationScopeValue(debug.living_chat_selected_organization);
|
||||
}
|
||||
|
||||
const directKnownOrganizations = Array.isArray(debug.assistant_known_organizations)
|
||||
? debug.assistant_known_organizations
|
||||
: [];
|
||||
const probeOrganizations = Array.isArray(debug.living_chat_data_scope_probe_organizations)
|
||||
? debug.living_chat_data_scope_probe_organizations
|
||||
: [];
|
||||
const organizationCandidates = Array.isArray(debug.organization_candidates) ? debug.organization_candidates : [];
|
||||
const directResolvedOrganizations = [
|
||||
debug.assistant_active_organization,
|
||||
debug.living_chat_selected_organization,
|
||||
readAddressDebugOrganization(debug, normalizeOrganizationScopeValue)
|
||||
];
|
||||
|
||||
knownOrganizationsFromAssistantHistory.push(
|
||||
...directKnownOrganizations,
|
||||
...probeOrganizations,
|
||||
...organizationCandidates,
|
||||
...directResolvedOrganizations
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
lastAssistantSelectedOrganization,
|
||||
lastAssistantActiveOrganization,
|
||||
knownOrganizationsFromAssistantHistory: mergeKnownOrganizationsDefault(knownOrganizationsFromAssistantHistory)
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveAssistantOrganizationAuthority(
|
||||
input: AssistantOrganizationAuthorityInput
|
||||
): AssistantOrganizationAuthority {
|
||||
const normalizeOrganizationScopeValue =
|
||||
input.normalizeOrganizationScopeValue ?? normalizeOrganizationScopeDefault;
|
||||
const mergeKnownOrganizations = input.mergeKnownOrganizations ?? mergeKnownOrganizationsDefault;
|
||||
const sessionItems = Array.isArray(input.sessionItems) ? input.sessionItems : [];
|
||||
const continuitySnapshot = resolveAssistantContinuitySnapshot({
|
||||
sessionItems,
|
||||
toNonEmptyString: input.toNonEmptyString ?? normalizeOrganizationScopeValue
|
||||
});
|
||||
const assistantSignals = collectAssistantOrganizationSignals(sessionItems, normalizeOrganizationScopeValue);
|
||||
const continuityActiveOrganization =
|
||||
normalizeOrganizationScopeValue(input.sessionActiveOrganization) ??
|
||||
assistantSignals.lastAssistantActiveOrganization ??
|
||||
normalizeOrganizationScopeValue(continuitySnapshot.activeOrganization);
|
||||
const knownOrganizations = mergeKnownOrganizations([
|
||||
...(Array.isArray(input.sessionKnownOrganizations) ? input.sessionKnownOrganizations : []),
|
||||
...assistantSignals.knownOrganizationsFromAssistantHistory,
|
||||
input.sessionSelectedOrganization,
|
||||
input.sessionActiveOrganization,
|
||||
continuityActiveOrganization
|
||||
]);
|
||||
const selectedOrganization =
|
||||
normalizeOrganizationScopeValue(input.sessionSelectedOrganization) ??
|
||||
assistantSignals.lastAssistantSelectedOrganization;
|
||||
const activeOrganization =
|
||||
normalizeOrganizationScopeValue(input.sessionActiveOrganization) ??
|
||||
selectedOrganization ??
|
||||
assistantSignals.lastAssistantActiveOrganization ??
|
||||
continuityActiveOrganization ??
|
||||
(knownOrganizations.length === 1 ? knownOrganizations[0] : null);
|
||||
const organizationClarificationCandidates = Array.isArray(input.lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? mergeKnownOrganizations([
|
||||
...input.lastOrganizationClarificationDebug.organization_candidates,
|
||||
...knownOrganizations,
|
||||
selectedOrganization,
|
||||
activeOrganization,
|
||||
continuityActiveOrganization
|
||||
])
|
||||
: [];
|
||||
const organizationClarificationSelectionFromScope = selectedOrganization ?? activeOrganization;
|
||||
|
||||
return {
|
||||
continuitySnapshot,
|
||||
continuityActiveOrganization,
|
||||
lastAssistantSelectedOrganization: assistantSignals.lastAssistantSelectedOrganization,
|
||||
lastAssistantActiveOrganization: assistantSignals.lastAssistantActiveOrganization,
|
||||
knownOrganizations,
|
||||
selectedOrganization,
|
||||
activeOrganization,
|
||||
organizationClarificationCandidates,
|
||||
organizationClarificationSelectionFromScope
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ import {
|
||||
normalizeOrganizationScopeValue
|
||||
} from "./assistantOrganizationMatcher";
|
||||
import {
|
||||
resolveAssistantContinuitySnapshot,
|
||||
isGroundedAddressDebug,
|
||||
readAddressDebugOrganization
|
||||
resolveAssistantOrganizationAuthority
|
||||
} from "./assistantContinuityPolicy";
|
||||
|
||||
const DATA_SCOPE_CACHE_TTL_MS = 60_000;
|
||||
@@ -297,40 +295,13 @@ export function createAssistantDataScopePolicy(deps: AssistantDataScopePolicyDep
|
||||
}
|
||||
|
||||
function findLastAssistantActiveOrganization(items: unknown[]): string | null {
|
||||
const continuitySnapshot = resolveAssistantContinuitySnapshot({
|
||||
const authority = resolveAssistantOrganizationAuthority({
|
||||
sessionItems: items,
|
||||
toNonEmptyString: normalizeOrganizationScopeValue
|
||||
toNonEmptyString: normalizeOrganizationScopeValue,
|
||||
normalizeOrganizationScopeValue,
|
||||
mergeKnownOrganizations
|
||||
});
|
||||
const continuityOrganization = normalizeOrganizationScopeValue(continuitySnapshot.activeOrganization);
|
||||
|
||||
for (let index = (Array.isArray(items) ? items.length : 0) - 1; index >= 0; index -= 1) {
|
||||
const item = Array.isArray(items) ? items[index] : null;
|
||||
if (!item || typeof item !== "object" || (item as { role?: string }).role !== "assistant") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const debug = (item as { debug?: Record<string, unknown> }).debug;
|
||||
if (!debug || typeof debug !== "object") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const direct = normalizeOrganizationScopeValue(debug.assistant_active_organization);
|
||||
if (direct) {
|
||||
return direct;
|
||||
}
|
||||
const selected = normalizeOrganizationScopeValue(debug.living_chat_selected_organization);
|
||||
if (selected) {
|
||||
return selected;
|
||||
}
|
||||
if (isGroundedAddressDebug(debug)) {
|
||||
const groundedOrganization = normalizeOrganizationScopeValue(readAddressDebugOrganization(debug));
|
||||
if (groundedOrganization) {
|
||||
return groundedOrganization;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return continuityOrganization;
|
||||
return normalizeOrganizationScopeValue(authority.activeOrganization);
|
||||
}
|
||||
|
||||
function extractOrganizationFactsFromRows(rows: unknown[]): AssistantOrganizationFacts {
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
buildInventoryHistoryCapabilityFollowupReply as buildInventoryHistoryCapabilityFollowupReplyFromPolicy,
|
||||
resolveAssistantLivingChatMemoryContext
|
||||
} from "./assistantMemoryRecapPolicy";
|
||||
import { resolveAssistantContinuitySnapshot } from "./assistantContinuityPolicy";
|
||||
import { resolveAssistantOrganizationAuthority } from "./assistantContinuityPolicy";
|
||||
|
||||
export interface AssistantLivingChatSessionScopeInput {
|
||||
knownOrganizations?: unknown[];
|
||||
@@ -249,10 +249,18 @@ export async function runAssistantLivingChatRuntime(
|
||||
input: AssistantLivingChatRuntimeInput
|
||||
): Promise<AssistantLivingChatRuntimeOutput> {
|
||||
const userMessage = String(input.userMessage ?? "");
|
||||
const continuitySnapshot = resolveAssistantContinuitySnapshot({
|
||||
const organizationAuthority = resolveAssistantOrganizationAuthority({
|
||||
sessionItems: input.sessionItems,
|
||||
toNonEmptyString: input.toNonEmptyString
|
||||
sessionKnownOrganizations: Array.isArray(input.sessionScope.knownOrganizations)
|
||||
? input.sessionScope.knownOrganizations
|
||||
: [],
|
||||
sessionSelectedOrganization: input.sessionScope.selectedOrganization,
|
||||
sessionActiveOrganization: input.sessionScope.activeOrganization,
|
||||
toNonEmptyString: input.toNonEmptyString,
|
||||
normalizeOrganizationScopeValue: input.toNonEmptyString,
|
||||
mergeKnownOrganizations: input.mergeKnownOrganizations
|
||||
});
|
||||
const continuitySnapshot = organizationAuthority.continuitySnapshot;
|
||||
const dataScopeMetaQuery = input.hasAssistantDataScopeMetaQuestionSignal(userMessage);
|
||||
const capabilityMetaQuery = input.shouldHandleAsAssistantCapabilityMetaQuery(userMessage);
|
||||
const destructiveSignal = input.hasDestructiveDataActionSignal(userMessage);
|
||||
@@ -267,13 +275,10 @@ export async function runAssistantLivingChatRuntime(
|
||||
let livingChatGroundingGuardApplied = false;
|
||||
let livingChatGroundingGuardReason: string | null = null;
|
||||
let livingChatProactiveScopeOfferApplied = false;
|
||||
const continuityActiveOrganization = input.toNonEmptyString(continuitySnapshot.activeOrganization);
|
||||
let knownOrganizations = input.mergeKnownOrganizations([
|
||||
...(Array.isArray(input.sessionScope.knownOrganizations) ? input.sessionScope.knownOrganizations : []),
|
||||
continuityActiveOrganization
|
||||
]);
|
||||
let selectedOrganization = input.toNonEmptyString(input.sessionScope.selectedOrganization);
|
||||
let activeOrganization = input.toNonEmptyString(input.sessionScope.activeOrganization) ?? continuityActiveOrganization;
|
||||
const continuityActiveOrganization = organizationAuthority.continuityActiveOrganization;
|
||||
let knownOrganizations = [...organizationAuthority.knownOrganizations];
|
||||
let selectedOrganization = organizationAuthority.selectedOrganization;
|
||||
let activeOrganization = organizationAuthority.activeOrganization;
|
||||
const memoryRecapContext = resolveAssistantLivingChatMemoryContext({
|
||||
modeDecisionReason: input.modeDecision?.reason ?? null,
|
||||
sessionItems: input.sessionItems
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import { resolveAssistantContinuitySnapshot } from "./assistantContinuityPolicy";
|
||||
import {
|
||||
resolveAssistantOrganizationAuthority
|
||||
} from "./assistantContinuityPolicy";
|
||||
|
||||
const ADDRESS_INTENTS_KEEP_ADDRESS_LANE = new Set([
|
||||
"period_coverage_profile",
|
||||
@@ -109,26 +111,25 @@ export function createAssistantRoutePolicy(deps) {
|
||||
const sessionOrganizationScope = input?.sessionOrganizationScope && typeof input.sessionOrganizationScope === "object"
|
||||
? input.sessionOrganizationScope
|
||||
: null;
|
||||
const continuitySnapshot = resolveAssistantContinuitySnapshot({
|
||||
const organizationAuthority = resolveAssistantOrganizationAuthority({
|
||||
sessionItems,
|
||||
toNonEmptyString
|
||||
sessionKnownOrganizations: Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||
? sessionOrganizationScope.knownOrganizations
|
||||
: [],
|
||||
sessionSelectedOrganization: sessionOrganizationScope?.selectedOrganization,
|
||||
sessionActiveOrganization: sessionOrganizationScope?.activeOrganization,
|
||||
lastOrganizationClarificationDebug: findLastOrganizationClarificationAddressDebug(sessionItems),
|
||||
toNonEmptyString,
|
||||
normalizeOrganizationScopeValue,
|
||||
mergeKnownOrganizations
|
||||
});
|
||||
const continuityActiveOrganization = normalizeOrganizationScopeValue(sessionOrganizationScope?.activeOrganization) ??
|
||||
normalizeOrganizationScopeValue(continuitySnapshot.activeOrganization);
|
||||
const continuitySnapshot = organizationAuthority.continuitySnapshot;
|
||||
const continuityActiveOrganization = organizationAuthority.continuityActiveOrganization;
|
||||
const lastGroundedAddressDebug = findLastGroundedAddressAnswerDebug(sessionItems) ??
|
||||
continuitySnapshot.lastGroundedAddressDebug;
|
||||
const lastOrganizationClarificationDebug = findLastOrganizationClarificationAddressDebug(sessionItems);
|
||||
const organizationClarificationCandidates = Array.isArray(lastOrganizationClarificationDebug?.organization_candidates)
|
||||
? mergeKnownOrganizations([
|
||||
...lastOrganizationClarificationDebug.organization_candidates,
|
||||
...((Array.isArray(sessionOrganizationScope?.knownOrganizations)
|
||||
? sessionOrganizationScope.knownOrganizations
|
||||
: [])),
|
||||
continuityActiveOrganization
|
||||
])
|
||||
: [];
|
||||
const organizationClarificationSelectionFromScope = normalizeOrganizationScopeValue(sessionOrganizationScope?.selectedOrganization) ??
|
||||
continuityActiveOrganization;
|
||||
const organizationClarificationCandidates = organizationAuthority.organizationClarificationCandidates;
|
||||
const organizationClarificationSelectionFromScope = organizationAuthority.organizationClarificationSelectionFromScope;
|
||||
const organizationClarificationSelection = resolveOrganizationSelectionFromMessage(rawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(repairedRawUserMessage, organizationClarificationCandidates) ??
|
||||
resolveOrganizationSelectionFromMessage(effectiveAddressUserMessage, organizationClarificationCandidates) ??
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveAssistantOrganizationAuthority } from "../src/services/assistantContinuityPolicy";
|
||||
|
||||
describe("assistantContinuityPolicy organization authority", () => {
|
||||
it("prefers explicit assistant organization authority over older grounded continuity and merges known organizations once", () => {
|
||||
const authority = resolveAssistantOrganizationAuthority({
|
||||
sessionItems: [
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "address_query",
|
||||
answer_grounding_check: { status: "grounded" },
|
||||
extracted_filters: {
|
||||
organization: "Org Grounded",
|
||||
as_of_date: "2020-03-31"
|
||||
},
|
||||
detected_intent: "receivables_confirmed_as_of_date"
|
||||
}
|
||||
},
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "living_chat",
|
||||
living_chat_selected_organization: "Org Selected",
|
||||
assistant_active_organization: "Org Selected",
|
||||
assistant_known_organizations: ["Org Selected", "Org Known"],
|
||||
living_chat_data_scope_probe_organizations: ["Org Probe"]
|
||||
}
|
||||
}
|
||||
],
|
||||
sessionKnownOrganizations: ["Org Session"],
|
||||
lastOrganizationClarificationDebug: {
|
||||
organization_candidates: ["Org Candidate", "Org Selected"]
|
||||
}
|
||||
});
|
||||
|
||||
expect(authority.continuitySnapshot.activeOrganization).toBe("Org Grounded");
|
||||
expect(authority.continuityActiveOrganization).toBe("Org Selected");
|
||||
expect(authority.selectedOrganization).toBe("Org Selected");
|
||||
expect(authority.activeOrganization).toBe("Org Selected");
|
||||
expect(authority.knownOrganizations).toEqual([
|
||||
"Org Session",
|
||||
"Org Selected",
|
||||
"Org Known",
|
||||
"Org Probe",
|
||||
"Org Grounded"
|
||||
]);
|
||||
expect(authority.organizationClarificationCandidates).toEqual([
|
||||
"Org Candidate",
|
||||
"Org Selected",
|
||||
"Org Session",
|
||||
"Org Known",
|
||||
"Org Probe",
|
||||
"Org Grounded"
|
||||
]);
|
||||
expect(authority.organizationClarificationSelectionFromScope).toBe("Org Selected");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createAssistantDataScopePolicy } from "../src/services/assistantDataScopePolicy";
|
||||
|
||||
function createPolicy() {
|
||||
return createAssistantDataScopePolicy({
|
||||
activeMcpChannel: "default",
|
||||
mcpProxyUrl: "http://localhost",
|
||||
executeAddressMcpQuery: vi.fn(async () => ({ rows: [] })),
|
||||
repairAddressMojibake: (value: unknown) => String(value ?? "")
|
||||
});
|
||||
}
|
||||
|
||||
describe("assistantDataScopePolicy", () => {
|
||||
it("recovers active organization from assistant-side living chat authority when grounded answer is absent", () => {
|
||||
const policy = createPolicy();
|
||||
|
||||
const activeOrganization = policy.findLastAssistantActiveOrganization([
|
||||
{
|
||||
role: "assistant",
|
||||
debug: {
|
||||
execution_lane: "living_chat",
|
||||
living_chat_selected_organization: "Org Selected",
|
||||
assistant_active_organization: "Org Selected",
|
||||
assistant_known_organizations: ["Org Selected", "Org Backup"]
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
expect(activeOrganization).toBe("Org Selected");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user