ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов 2.25 финализацию address-ветки в отдельный adapter \ Переподключил assistantService на новый adapter (без смены поведения)

This commit is contained in:
2026-04-10 19:37:56 +03:00
parent 80d108e506
commit 205daeccc5
9 changed files with 794 additions and 209 deletions
@@ -0,0 +1,190 @@
import { nanoid } from "nanoid";
import type {
AssistantConversationItem,
AssistantDebugPayload,
AssistantMessageResponsePayload,
AssistantReplyType
} from "../types/assistant";
import type { AddressExecutionDebug } from "../types/addressQuery";
import type { CommitAssistantTurnAndLogOutput } from "./assistantTurnCommitRuntimeAdapter";
import { commitAssistantTurnAndLog } from "./assistantTurnCommitRuntimeAdapter";
export interface AddressCarryoverMetaLogInput {
previousAddressIntent?: string | null;
previousAddressAnchor?: string | null;
}
export interface AddressLlmPreDecomposeMetaLogInput {
attempted?: boolean;
applied?: boolean;
provider?: string | null;
traceId?: string | null;
reason?: string | null;
fallbackRuleHit?: string | null;
sanitizedUserMessage?: string | null;
toolGateDecision?: string | null;
toolGateReason?: string | null;
dialogContinuationContract?: {
decision?: string | null;
target_intent?: string | null;
} | null;
addressRetryAudit?: {
attempted?: boolean;
reason?: string | null;
initial_limited_category?: string | null;
retry_result_category?: string | null;
} | null;
predecomposeContract?: {
intent?: string | null;
aggregation_profile?: string | null;
period?: {
scope?: string | null;
} | null;
} | null;
}
export interface FinalizeAssistantAddressTurnInput {
sessionId: string;
userMessage: string;
effectiveAddressUserMessage: string;
assistantReply: string;
replyType: AssistantReplyType;
addressLaneDebug: AddressExecutionDebug;
debug: AssistantDebugPayload | Record<string, unknown>;
carryoverMeta?: AddressCarryoverMetaLogInput | null;
llmPreDecomposeMeta?: AddressLlmPreDecomposeMetaLogInput | null;
appendItem: Parameters<typeof commitAssistantTurnAndLog>[0]["appendItem"];
getSession: Parameters<typeof commitAssistantTurnAndLog>[0]["getSession"];
persistSession: Parameters<typeof commitAssistantTurnAndLog>[0]["persistSession"];
cloneConversation: Parameters<typeof commitAssistantTurnAndLog>[0]["cloneConversation"];
logEvent: Parameters<typeof commitAssistantTurnAndLog>[0]["logEvent"];
nowIso?: () => string;
messageIdFactory?: () => string;
commitFn?: typeof commitAssistantTurnAndLog;
}
export interface FinalizeAssistantAddressTurnOutput {
assistantItem: AssistantConversationItem;
commitResult: CommitAssistantTurnAndLogOutput;
response: AssistantMessageResponsePayload;
}
function toTraceId(debug: AssistantDebugPayload | Record<string, unknown>): string | null {
const value = (debug as Record<string, unknown> | null | undefined)?.trace_id;
if (typeof value !== "string") {
return null;
}
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : null;
}
function buildAddressProcessedLogDetails(input: FinalizeAssistantAddressTurnInput, assistantItem: AssistantConversationItem) {
const laneDebug = input.addressLaneDebug;
const llmMeta = input.llmPreDecomposeMeta;
const carryover = input.carryoverMeta;
return {
session_id: input.sessionId,
message_id: assistantItem.message_id,
user_message: input.userMessage,
effective_address_user_message: input.effectiveAddressUserMessage,
address_followup_context_applied: Boolean(carryover),
address_followup_context_previous_intent: carryover?.previousAddressIntent ?? null,
address_followup_context_previous_anchor: carryover?.previousAddressAnchor ?? null,
address_llm_predecompose_attempted: Boolean(llmMeta?.attempted),
address_llm_predecompose_applied: Boolean(llmMeta?.applied),
address_llm_predecompose_provider: llmMeta?.provider ?? null,
address_llm_predecompose_trace_id: llmMeta?.traceId ?? null,
address_llm_predecompose_reason: llmMeta?.reason ?? null,
address_fallback_rule_hit: llmMeta?.fallbackRuleHit ?? null,
address_sanitized_user_message: llmMeta?.sanitizedUserMessage ?? null,
address_tool_gate_decision: llmMeta?.toolGateDecision ?? null,
address_tool_gate_reason: llmMeta?.toolGateReason ?? null,
address_dialog_continuation_decision: llmMeta?.dialogContinuationContract?.decision ?? null,
address_dialog_continuation_target_intent: llmMeta?.dialogContinuationContract?.target_intent ?? null,
address_retry_attempted: Boolean(llmMeta?.addressRetryAudit?.attempted),
address_retry_reason: llmMeta?.addressRetryAudit?.reason ?? null,
address_retry_initial_limited_category: llmMeta?.addressRetryAudit?.initial_limited_category ?? null,
address_retry_result_category: llmMeta?.addressRetryAudit?.retry_result_category ?? null,
address_llm_predecompose_contract_intent: llmMeta?.predecomposeContract?.intent ?? null,
address_llm_predecompose_contract_aggregation_profile: llmMeta?.predecomposeContract?.aggregation_profile ?? null,
address_llm_predecompose_contract_period_scope: llmMeta?.predecomposeContract?.period?.scope ?? null,
detected_mode: laneDebug.detected_mode,
query_shape: laneDebug.query_shape,
detected_intent: laneDebug.detected_intent,
extracted_filters: laneDebug.extracted_filters,
selected_recipe: laneDebug.selected_recipe,
mcp_call_status_legacy: laneDebug.mcp_call_status_legacy,
account_scope_mode: laneDebug.account_scope_mode,
account_scope_fallback_applied: laneDebug.account_scope_fallback_applied,
anchor_type: laneDebug.anchor_type,
resolver_confidence: laneDebug.resolver_confidence,
match_failure_stage: laneDebug.match_failure_stage,
match_failure_reason: laneDebug.match_failure_reason,
mcp_call_status: laneDebug.mcp_call_status,
rows_fetched: laneDebug.rows_fetched,
raw_rows_received: laneDebug.raw_rows_received,
rows_after_account_scope: laneDebug.rows_after_account_scope,
rows_after_recipe_filter: laneDebug.rows_after_recipe_filter,
rows_materialized: laneDebug.rows_materialized,
rows_matched: laneDebug.rows_matched,
materialization_drop_reason: laneDebug.materialization_drop_reason,
account_token_raw: laneDebug.account_token_raw,
account_token_normalized: laneDebug.account_token_normalized,
account_scope_fields_checked: laneDebug.account_scope_fields_checked,
account_scope_match_strategy: laneDebug.account_scope_match_strategy,
account_scope_drop_reason: laneDebug.account_scope_drop_reason,
runtime_readiness: laneDebug.runtime_readiness,
limited_reason_category: laneDebug.limited_reason_category,
response_type: laneDebug.response_type,
limitations: laneDebug.limitations,
assistant_reply: assistantItem.text,
reply_type: assistantItem.reply_type,
trace_id: assistantItem.trace_id
};
}
export function finalizeAssistantAddressTurn(
input: FinalizeAssistantAddressTurnInput
): FinalizeAssistantAddressTurnOutput {
const nowIso = input.nowIso ?? (() => new Date().toISOString());
const messageIdFactory = input.messageIdFactory ?? (() => `msg-${nanoid(10)}`);
const commitSafe = input.commitFn ?? commitAssistantTurnAndLog;
const assistantItem: AssistantConversationItem = {
message_id: messageIdFactory(),
session_id: input.sessionId,
role: "assistant",
text: input.assistantReply,
reply_type: input.replyType,
created_at: nowIso(),
trace_id: toTraceId(input.debug),
debug: input.debug as AssistantDebugPayload
};
const logDetails = buildAddressProcessedLogDetails(input, assistantItem);
const commitResult = commitSafe({
sessionId: input.sessionId,
assistantItem,
eventType: "assistant_message_address",
logDetails,
appendItem: input.appendItem,
getSession: input.getSession,
persistSession: input.persistSession,
cloneConversation: input.cloneConversation,
logEvent: input.logEvent
});
const response: AssistantMessageResponsePayload = {
ok: true,
session_id: input.sessionId,
assistant_reply: assistantItem.text,
reply_type: assistantItem.reply_type as AssistantReplyType,
conversation_item: assistantItem,
debug: input.debug as AssistantDebugPayload,
conversation: commitResult.conversation
};
return {
assistantItem,
commitResult,
response
};
}
@@ -0,0 +1,57 @@
import type { AssistantConversationItem, AssistantDebugPayload, AssistantMessageResponsePayload, AssistantReplyType } from "../types/assistant";
import { buildAssistantDeepTurnSuccessResponse } from "./assistantDeepTurnResponseBuilder";
import type { CommitAssistantTurnAndLogOutput } from "./assistantTurnCommitRuntimeAdapter";
import { commitAssistantTurnAndLog } from "./assistantTurnCommitRuntimeAdapter";
export interface FinalizeAssistantDeepTurnInput {
sessionId: string;
assistantReply: string;
replyType: AssistantReplyType;
assistantItem: AssistantConversationItem;
debug: AssistantDebugPayload | Record<string, unknown>;
deepAnalysisLogDetails: Record<string, unknown>;
appendItem: Parameters<typeof commitAssistantTurnAndLog>[0]["appendItem"];
getSession: Parameters<typeof commitAssistantTurnAndLog>[0]["getSession"];
persistSession: Parameters<typeof commitAssistantTurnAndLog>[0]["persistSession"];
cloneConversation: Parameters<typeof commitAssistantTurnAndLog>[0]["cloneConversation"];
logEvent: Parameters<typeof commitAssistantTurnAndLog>[0]["logEvent"];
commitFn?: typeof commitAssistantTurnAndLog;
buildResponseFn?: typeof buildAssistantDeepTurnSuccessResponse;
}
export interface FinalizeAssistantDeepTurnOutput {
commitResult: CommitAssistantTurnAndLogOutput;
response: AssistantMessageResponsePayload;
}
export function finalizeAssistantDeepTurn(
input: FinalizeAssistantDeepTurnInput
): FinalizeAssistantDeepTurnOutput {
const commitSafe = input.commitFn ?? commitAssistantTurnAndLog;
const buildResponseSafe = input.buildResponseFn ?? buildAssistantDeepTurnSuccessResponse;
const commitResult = commitSafe({
sessionId: input.sessionId,
assistantItem: input.assistantItem,
eventType: "assistant_message",
logDetails: input.deepAnalysisLogDetails,
appendItem: input.appendItem,
getSession: input.getSession,
persistSession: input.persistSession,
cloneConversation: input.cloneConversation,
logEvent: input.logEvent
});
const response = buildResponseSafe({
sessionId: input.sessionId,
assistantReply: input.assistantReply,
replyType: input.replyType,
conversationItem: input.assistantItem,
debug: input.debug,
conversation: commitResult.conversation
});
return {
commitResult,
response
};
}
@@ -19,17 +19,17 @@ import * as openaiResponsesClient_1 from "./openaiResponsesClient";
import * as addressMcpClient_1 from "./addressMcpClient";
import * as capabilitiesRegistry_1 from "./capabilitiesRegistry";
import * as assistantCanon_1 from "./assistantCanon";
import * as assistantAddressTurnFinalizeRuntimeAdapter_1 from "./assistantAddressTurnFinalizeRuntimeAdapter";
import * as assistantCoverageGrounding_1 from "./assistantCoverageGrounding";
import * as assistantDeepTurnResponseBuilder_1 from "./assistantDeepTurnResponseBuilder";
import * as assistantDeepTurnCompositionRuntimeAdapter_1 from "./assistantDeepTurnCompositionRuntimeAdapter";
import * as assistantDeepTurnContextRuntimeAdapter_1 from "./assistantDeepTurnContextRuntimeAdapter";
import * as assistantDeepTurnFinalizeRuntimeAdapter_1 from "./assistantDeepTurnFinalizeRuntimeAdapter";
import * as assistantDeepTurnGuardRuntimeAdapter_1 from "./assistantDeepTurnGuardRuntimeAdapter";
import * as assistantDeepTurnGroundingRuntimeAdapter_1 from "./assistantDeepTurnGroundingRuntimeAdapter";
import * as assistantDeepTurnPackagingRuntimeAdapter_1 from "./assistantDeepTurnPackagingRuntimeAdapter";
import * as assistantDeepTurnPlanRuntimeAdapter_1 from "./assistantDeepTurnPlanRuntimeAdapter";
import * as assistantDeepTurnRetrievalRuntimeAdapter_1 from "./assistantDeepTurnRetrievalRuntimeAdapter";
import * as assistantQueryPlanning_1 from "./assistantQueryPlanning";
import * as assistantTurnCommitRuntimeAdapter_1 from "./assistantTurnCommitRuntimeAdapter";
import iconv from "iconv-lite";
const DATA_SCOPE_CACHE_TTL_MS = 60_000;
const dataScopeProbeCache = new Map();
@@ -4394,98 +4394,24 @@ export class AssistantService {
if (debugActiveOrganization) {
debug.assistant_active_organization = debugActiveOrganization;
}
const assistantItem = {
message_id: `msg-${(0, nanoid_1.nanoid)(10)}`,
session_id: sessionId,
role: "assistant",
text: safeAddressReply,
reply_type: addressLane.reply_type,
created_at: new Date().toISOString(),
trace_id: debug.trace_id,
debug
};
this.sessions.appendItem(sessionId, assistantItem);
const current = this.sessions.getSession(sessionId);
if (current) {
this.sessionLogger.persistSession(current);
}
const conversation = cloneItems(current?.items ?? []);
(0, log_1.logJson)({
timestamp: new Date().toISOString(),
level: "info",
service: "assistant_loop",
message: "assistant_message_processed",
const finalization = (0, assistantAddressTurnFinalizeRuntimeAdapter_1.finalizeAssistantAddressTurn)({
sessionId,
eventType: "assistant_message_address",
details: {
session_id: sessionId,
message_id: assistantItem.message_id,
user_message: userMessage,
effective_address_user_message: effectiveAddressUserMessage,
address_followup_context_applied: Boolean(carryoverMeta),
address_followup_context_previous_intent: carryoverMeta?.previousAddressIntent ?? null,
address_followup_context_previous_anchor: carryoverMeta?.previousAddressAnchor ?? null,
address_llm_predecompose_attempted: Boolean(llmPreDecomposeMeta?.attempted),
address_llm_predecompose_applied: Boolean(llmPreDecomposeMeta?.applied),
address_llm_predecompose_provider: llmPreDecomposeMeta?.provider ?? null,
address_llm_predecompose_trace_id: llmPreDecomposeMeta?.traceId ?? null,
address_llm_predecompose_reason: llmPreDecomposeMeta?.reason ?? null,
address_fallback_rule_hit: llmPreDecomposeMeta?.fallbackRuleHit ?? null,
address_sanitized_user_message: llmPreDecomposeMeta?.sanitizedUserMessage ?? null,
address_tool_gate_decision: llmPreDecomposeMeta?.toolGateDecision ?? null,
address_tool_gate_reason: llmPreDecomposeMeta?.toolGateReason ?? null,
address_dialog_continuation_decision: llmPreDecomposeMeta?.dialogContinuationContract?.decision ?? null,
address_dialog_continuation_target_intent: llmPreDecomposeMeta?.dialogContinuationContract?.target_intent ?? null,
address_retry_attempted: Boolean(llmPreDecomposeMeta?.addressRetryAudit?.attempted),
address_retry_reason: llmPreDecomposeMeta?.addressRetryAudit?.reason ?? null,
address_retry_initial_limited_category: llmPreDecomposeMeta?.addressRetryAudit?.initial_limited_category ?? null,
address_retry_result_category: llmPreDecomposeMeta?.addressRetryAudit?.retry_result_category ?? null,
address_llm_predecompose_contract_intent: llmPreDecomposeMeta?.predecomposeContract?.intent ?? null,
address_llm_predecompose_contract_aggregation_profile: llmPreDecomposeMeta?.predecomposeContract?.aggregation_profile ?? null,
address_llm_predecompose_contract_period_scope: llmPreDecomposeMeta?.predecomposeContract?.period?.scope ?? null,
detected_mode: addressLane.debug.detected_mode,
query_shape: addressLane.debug.query_shape,
detected_intent: addressLane.debug.detected_intent,
extracted_filters: addressLane.debug.extracted_filters,
selected_recipe: addressLane.debug.selected_recipe,
mcp_call_status_legacy: addressLane.debug.mcp_call_status_legacy,
account_scope_mode: addressLane.debug.account_scope_mode,
account_scope_fallback_applied: addressLane.debug.account_scope_fallback_applied,
anchor_type: addressLane.debug.anchor_type,
resolver_confidence: addressLane.debug.resolver_confidence,
match_failure_stage: addressLane.debug.match_failure_stage,
match_failure_reason: addressLane.debug.match_failure_reason,
mcp_call_status: addressLane.debug.mcp_call_status,
rows_fetched: addressLane.debug.rows_fetched,
raw_rows_received: addressLane.debug.raw_rows_received,
rows_after_account_scope: addressLane.debug.rows_after_account_scope,
rows_after_recipe_filter: addressLane.debug.rows_after_recipe_filter,
rows_materialized: addressLane.debug.rows_materialized,
rows_matched: addressLane.debug.rows_matched,
materialization_drop_reason: addressLane.debug.materialization_drop_reason,
account_token_raw: addressLane.debug.account_token_raw,
account_token_normalized: addressLane.debug.account_token_normalized,
account_scope_fields_checked: addressLane.debug.account_scope_fields_checked,
account_scope_match_strategy: addressLane.debug.account_scope_match_strategy,
account_scope_drop_reason: addressLane.debug.account_scope_drop_reason,
runtime_readiness: addressLane.debug.runtime_readiness,
limited_reason_category: addressLane.debug.limited_reason_category,
response_type: addressLane.debug.response_type,
limitations: addressLane.debug.limitations,
assistant_reply: assistantItem.text,
reply_type: assistantItem.reply_type,
trace_id: assistantItem.trace_id
}
});
return {
ok: true,
session_id: sessionId,
assistant_reply: assistantItem.text,
reply_type: assistantItem.reply_type,
conversation_item: assistantItem,
userMessage,
effectiveAddressUserMessage,
assistantReply: safeAddressReply,
replyType: addressLane.reply_type,
addressLaneDebug: addressLane.debug,
debug,
conversation
};
carryoverMeta,
llmPreDecomposeMeta,
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
cloneConversation: (items) => cloneItems(items),
logEvent: (payload) => (0, log_1.logJson)(payload),
messageIdFactory: () => `msg-${(0, nanoid_1.nanoid)(10)}`
});
return finalization.response;
};
const tryHandleLivingChat = async (modeDecision, addressRuntimeMeta = null) => {
try {
@@ -5053,26 +4979,20 @@ export class AssistantService {
const debug = packagingRuntime.debug;
const assistantItem = packagingRuntime.assistantItem;
const deepAnalysisLogDetails = packagingRuntime.deepAnalysisLogDetails;
const commitResult = (0, assistantTurnCommitRuntimeAdapter_1.commitAssistantTurnAndLog)({
const finalization = (0, assistantDeepTurnFinalizeRuntimeAdapter_1.finalizeAssistantDeepTurn)({
sessionId,
assistantReply: safeAssistantReply,
replyType: composition.reply_type,
assistantItem,
eventType: "assistant_message",
logDetails: deepAnalysisLogDetails,
debug,
deepAnalysisLogDetails,
appendItem: (targetSessionId, item) => this.sessions.appendItem(targetSessionId, item),
getSession: (targetSessionId) => this.sessions.getSession(targetSessionId),
persistSession: (sessionState) => this.sessionLogger.persistSession(sessionState),
cloneConversation: (items) => cloneItems(items),
logEvent: (payload) => (0, log_1.logJson)(payload)
});
const conversation = commitResult.conversation;
return (0, assistantDeepTurnResponseBuilder_1.buildAssistantDeepTurnSuccessResponse)({
sessionId,
assistantReply: safeAssistantReply,
replyType: composition.reply_type,
conversationItem: assistantItem,
debug,
conversation
});
return finalization.response;
}
}