АРЧ АП11 - Архитектура после регресса: Архитектура: централизовать organization authority в continuity policy для route, living-chat и data-scope
This commit is contained in:
@@ -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) ??
|
||||
|
||||
Reference in New Issue
Block a user