NODEDC_1C/llm_normalizer/backend/src/services/assistantAddressRuntimeAdap...

204 lines
8.8 KiB
TypeScript

import {
buildAssistantAddressOrchestrationRuntime,
type AssistantAddressCarryoverLike,
type BuildAssistantAddressOrchestrationRuntimeInput,
type BuildAssistantAddressOrchestrationRuntimeOutput
} from "./assistantAddressOrchestrationRuntimeAdapter";
import {
runAssistantAddressLaneRuntime,
type AssistantAddressLaneLike,
type RunAssistantAddressLaneRuntimeOutput
} from "./assistantAddressLaneRuntimeAdapter";
import {
runAssistantAddressToolGateRuntime,
type AssistantAddressToolGateRuntimeOutput
} from "./assistantAddressToolGateRuntimeAdapter";
export interface RunAssistantAddressRuntimeInput<ResponseType = unknown> {
featureAssistantAddressQueryV1: boolean;
sessionId: string;
userMessage: string;
sessionItems: unknown[];
sessionAddressNavigationState?: unknown;
sessionOrganizationScope?: {
knownOrganizations?: unknown;
selectedOrganization?: unknown;
activeOrganization?: unknown;
} | null;
llmProvider: unknown;
useMock: boolean;
featureAddressLlmPredecomposeV1: boolean;
runAddressLlmPreDecompose: () => Promise<Record<string, unknown>>;
buildAddressLlmPredecomposeContractV1: BuildAssistantAddressOrchestrationRuntimeInput["buildAddressLlmPredecomposeContractV1"];
sanitizeAddressMessageForFallback: BuildAssistantAddressOrchestrationRuntimeInput["sanitizeAddressMessageForFallback"];
toNonEmptyString: (value: unknown) => string | null;
resolveAddressFollowupCarryoverContext: BuildAssistantAddressOrchestrationRuntimeInput["resolveAddressFollowupCarryoverContext"];
resolveAssistantOrchestrationDecision: BuildAssistantAddressOrchestrationRuntimeInput["resolveAssistantOrchestrationDecision"];
buildAddressDialogContinuationContractV2: BuildAssistantAddressOrchestrationRuntimeInput["buildAddressDialogContinuationContractV2"];
runtimeAnalysisContextAsOfDate: string | null;
payloadContextPeriodHint: unknown;
compactWhitespace: (value: string) => string;
runAddressLaneAttempt: (
messageUsed: string,
carryMeta: AssistantAddressCarryoverLike | null,
analysisDateHint: string | null,
llmSemanticHints?: Record<string, unknown> | null
) => Promise<AssistantAddressLaneLike | null>;
isRetryableAddressLimitedResult: (addressLane: AssistantAddressLaneLike | null | undefined) => boolean;
finalizeAddressLaneResponse: (
addressLane: AssistantAddressLaneLike,
effectiveAddressUserMessage: string,
carryoverMeta?: AssistantAddressCarryoverLike | null,
llmPreDecomposeMeta?: Record<string, unknown> | null
) => ResponseType;
tryHandleLivingChat: (
modeDecision: { mode?: unknown; reason?: unknown },
addressRuntimeMeta: Record<string, unknown> | null
) => Promise<ResponseType | null>;
logEvent: (payload: Record<string, unknown>) => void;
nowIso: () => string;
runAddressOrchestrationRuntime?: (
input: BuildAssistantAddressOrchestrationRuntimeInput
) => Promise<BuildAssistantAddressOrchestrationRuntimeOutput>;
runAddressToolGateRuntime?: (
input: {
sessionId: string;
userMessage: string;
addressInputMessage: string;
orchestrationDecision: BuildAssistantAddressOrchestrationRuntimeOutput["orchestrationDecision"];
livingModeDecision: BuildAssistantAddressOrchestrationRuntimeOutput["livingModeDecision"];
addressRuntimeMeta: BuildAssistantAddressOrchestrationRuntimeOutput["addressRuntimeMeta"];
logEvent: (payload: Record<string, unknown>) => void;
tryHandleLivingChat: (
modeDecision: { mode?: unknown; reason?: unknown },
addressRuntimeMeta: Record<string, unknown> | null
) => Promise<ResponseType | null>;
nowIso: () => string;
}
) => Promise<AssistantAddressToolGateRuntimeOutput<ResponseType>>;
runAddressLaneRuntime?: (
input: {
userMessage: string;
addressInputMessage: string;
carryover: AssistantAddressCarryoverLike | null;
shouldPreferContextualLane: boolean;
canRetryWithRawUserMessage: boolean;
runAddressLaneAttempt: (
messageUsed: string,
carryMeta: AssistantAddressCarryoverLike | null,
llmSemanticHints?: Record<string, unknown> | null
) => Promise<AssistantAddressLaneLike | null>;
isRetryableAddressLimitedResult: (addressLane: AssistantAddressLaneLike | null | undefined) => boolean;
}
) => Promise<RunAssistantAddressLaneRuntimeOutput>;
}
export interface RunAssistantAddressRuntimeOutput<ResponseType = unknown> {
handled: boolean;
response: ResponseType | null;
addressRuntimeMetaForDeep: Record<string, unknown> | null;
}
export async function runAssistantAddressRuntime<ResponseType = unknown>(
input: RunAssistantAddressRuntimeInput<ResponseType>
): Promise<RunAssistantAddressRuntimeOutput<ResponseType>> {
if (!input.featureAssistantAddressQueryV1) {
return {
handled: false,
response: null,
addressRuntimeMetaForDeep: null
};
}
const runAddressOrchestrationRuntimeSafe =
input.runAddressOrchestrationRuntime ?? buildAssistantAddressOrchestrationRuntime;
const runAddressToolGateRuntimeSafe = input.runAddressToolGateRuntime ?? runAssistantAddressToolGateRuntime;
const runAddressLaneRuntimeSafe = input.runAddressLaneRuntime ?? runAssistantAddressLaneRuntime;
const addressOrchestrationRuntime = await runAddressOrchestrationRuntimeSafe({
userMessage: input.userMessage,
sessionItems: input.sessionItems,
sessionAddressNavigationState: input.sessionAddressNavigationState,
sessionOrganizationScope: input.sessionOrganizationScope ?? null,
llmProvider: input.llmProvider,
useMock: input.useMock,
featureAddressLlmPredecomposeV1: input.featureAddressLlmPredecomposeV1,
runAddressLlmPreDecompose: input.runAddressLlmPreDecompose,
buildAddressLlmPredecomposeContractV1: input.buildAddressLlmPredecomposeContractV1,
sanitizeAddressMessageForFallback: input.sanitizeAddressMessageForFallback,
toNonEmptyString: input.toNonEmptyString,
resolveAddressFollowupCarryoverContext: input.resolveAddressFollowupCarryoverContext,
resolveAssistantOrchestrationDecision: input.resolveAssistantOrchestrationDecision,
buildAddressDialogContinuationContractV2: input.buildAddressDialogContinuationContractV2
});
const addressInputMessage = addressOrchestrationRuntime.addressInputMessage;
const carryover = addressOrchestrationRuntime.carryover;
const orchestrationDecision = addressOrchestrationRuntime.orchestrationDecision;
const addressRuntimeMeta = addressOrchestrationRuntime.addressRuntimeMeta;
const livingModeDecision = addressOrchestrationRuntime.livingModeDecision;
const addressRuntimeMetaForDeep = addressRuntimeMeta;
const toolGateRuntime = await runAddressToolGateRuntimeSafe({
sessionId: input.sessionId,
userMessage: input.userMessage,
addressInputMessage,
orchestrationDecision,
livingModeDecision,
addressRuntimeMeta,
logEvent: input.logEvent,
tryHandleLivingChat: input.tryHandleLivingChat,
nowIso: input.nowIso
});
if (toolGateRuntime.handled && toolGateRuntime.response) {
return {
handled: true,
response: toolGateRuntime.response,
addressRuntimeMetaForDeep
};
}
if (Boolean(orchestrationDecision.runAddressLane)) {
const shouldPreferContextualLane = Boolean(carryover?.followupContext);
const analysisDateHint = input.runtimeAnalysisContextAsOfDate ?? input.toNonEmptyString(input.payloadContextPeriodHint);
const canRetryWithRawUserMessage =
input.compactWhitespace(String(addressInputMessage ?? "").toLowerCase()) !==
input.compactWhitespace(String(input.userMessage ?? "").toLowerCase());
const addressLaneRuntime = await runAddressLaneRuntimeSafe({
userMessage: input.userMessage,
addressInputMessage,
carryover,
llmSemanticHints:
addressRuntimeMeta && typeof addressRuntimeMeta === "object"
? ((addressRuntimeMeta as { semanticHints?: unknown }).semanticHints as Record<string, unknown> | null) ?? null
: null,
shouldPreferContextualLane,
canRetryWithRawUserMessage,
runAddressLaneAttempt: (messageUsed, carryMeta, llmSemanticHints = null) =>
input.runAddressLaneAttempt(messageUsed, carryMeta, analysisDateHint, llmSemanticHints),
isRetryableAddressLimitedResult: input.isRetryableAddressLimitedResult
});
if (addressLaneRuntime.handled && addressLaneRuntime.selection) {
const response = input.finalizeAddressLaneResponse(
addressLaneRuntime.selection.addressLane,
addressLaneRuntime.selection.messageUsed,
addressLaneRuntime.selection.carryMeta,
{
...addressRuntimeMeta,
addressRetryAudit: { ...addressLaneRuntime.retryAudit }
}
);
return {
handled: true,
response,
addressRuntimeMetaForDeep
};
}
}
return {
handled: false,
response: null,
addressRuntimeMetaForDeep
};
}