ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов 2.28 - вынос retry-orchestration адресного лейна (контекстный прогон, primary, retry сырого вопроса, fallback limited) в отдельный адаптер.

This commit is contained in:
2026-04-10 20:21:53 +03:00
parent 9b1d1bff91
commit 3531f7ddfe
9 changed files with 814 additions and 235 deletions
@@ -0,0 +1,133 @@
export interface BuildAssistantAddressOrchestrationRuntimeInput {
userMessage: string;
sessionItems: unknown[];
llmProvider: unknown;
useMock: boolean;
featureAddressLlmPredecomposeV1: boolean;
runAddressLlmPreDecompose: () => Promise<Record<string, unknown>>;
buildAddressLlmPredecomposeContractV1: (input: {
sourceMessage: string;
canonicalMessage: string;
}) => unknown;
sanitizeAddressMessageForFallback: (userMessage: string) => string;
toNonEmptyString: (value: unknown) => string | null;
resolveAddressFollowupCarryoverContext: (
userMessage: string,
sessionItems: unknown[],
addressInputMessage: string,
addressPreDecompose: Record<string, unknown>
) => AssistantAddressCarryoverLike | null;
resolveAssistantOrchestrationDecision: (input: {
rawUserMessage: string;
effectiveAddressUserMessage: string;
followupContext: unknown;
llmPreDecomposeMeta: Record<string, unknown>;
useMock: boolean;
}) => Record<string, unknown>;
buildAddressDialogContinuationContractV2: (
userMessage: string,
addressInputMessage: string,
carryover: AssistantAddressCarryoverLike | null,
addressPreDecompose: Record<string, unknown>
) => unknown;
}
export interface AssistantAddressCarryoverLike {
followupContext?: unknown;
[key: string]: unknown;
}
export interface BuildAssistantAddressOrchestrationRuntimeOutput {
addressPreDecompose: Record<string, unknown>;
addressInputMessage: string;
carryover: AssistantAddressCarryoverLike | null;
orchestrationDecision: Record<string, unknown>;
addressRuntimeMeta: Record<string, unknown>;
livingModeDecision: {
mode: unknown;
reason: unknown;
};
}
function fallbackAddressPreDecompose(
userMessage: string,
llmProvider: unknown,
buildAddressLlmPredecomposeContractV1: BuildAssistantAddressOrchestrationRuntimeInput["buildAddressLlmPredecomposeContractV1"],
sanitizeAddressMessageForFallback: BuildAssistantAddressOrchestrationRuntimeInput["sanitizeAddressMessageForFallback"]
): Record<string, unknown> {
const provider =
llmProvider === "local" ? "local" : llmProvider === "openai" ? "openai" : null;
return {
attempted: false,
applied: false,
provider,
traceId: null,
effectiveMessage: userMessage,
reason: "disabled_by_feature_flag",
llmCanonicalCandidateDetected: false,
predecomposeContract: buildAddressLlmPredecomposeContractV1({
sourceMessage: userMessage,
canonicalMessage: userMessage
}),
fallbackRuleHit: null,
sanitizedUserMessage: sanitizeAddressMessageForFallback(userMessage),
toolGateDecision: null,
toolGateReason: null
};
}
export async function buildAssistantAddressOrchestrationRuntime(
input: BuildAssistantAddressOrchestrationRuntimeInput
): Promise<BuildAssistantAddressOrchestrationRuntimeOutput> {
const addressPreDecompose = input.featureAddressLlmPredecomposeV1
? await input.runAddressLlmPreDecompose()
: fallbackAddressPreDecompose(
input.userMessage,
input.llmProvider,
input.buildAddressLlmPredecomposeContractV1,
input.sanitizeAddressMessageForFallback
);
const addressInputMessage =
input.toNonEmptyString(addressPreDecompose?.effectiveMessage) ?? input.userMessage;
const carryover = input.resolveAddressFollowupCarryoverContext(
input.userMessage,
input.sessionItems,
addressInputMessage,
addressPreDecompose
);
const followupContext = carryover?.followupContext ?? null;
const orchestrationDecision = input.resolveAssistantOrchestrationDecision({
rawUserMessage: input.userMessage,
effectiveAddressUserMessage: addressInputMessage,
followupContext,
llmPreDecomposeMeta: addressPreDecompose,
useMock: input.useMock
});
const dialogContinuationContract = input.buildAddressDialogContinuationContractV2(
input.userMessage,
addressInputMessage,
carryover,
addressPreDecompose
);
const addressRuntimeMeta = {
...addressPreDecompose,
toolGateDecision: orchestrationDecision.toolGateDecision ?? null,
toolGateReason: orchestrationDecision.toolGateReason ?? null,
dialogContinuationContract,
orchestrationContract: orchestrationDecision.orchestrationContract ?? null
};
const livingModeDecision = {
mode: orchestrationDecision.livingMode,
reason: orchestrationDecision.livingReason
};
return {
addressPreDecompose,
addressInputMessage,
carryover,
orchestrationDecision,
addressRuntimeMeta,
livingModeDecision
};
}