ГЛОБАЛЬНЫЙ РЕФАКТОРИНГ АРХИТЕКТУРЫ - Рефакторинг этапов 2.36 вынос tryHandleLivingChat в отдельный runtime-handler чтобы убрать еще один большой локальный блок. новый living-chat handler-адаптер с try/catch + finalize + warn fallback
This commit is contained in:
+73
@@ -0,0 +1,73 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.tryHandleAssistantLivingChatRuntime = tryHandleAssistantLivingChatRuntime;
|
||||
const assistantLivingChatRuntimeAdapter_1 = require("./assistantLivingChatRuntimeAdapter");
|
||||
const assistantLivingChatTurnFinalizeRuntimeAdapter_1 = require("./assistantLivingChatTurnFinalizeRuntimeAdapter");
|
||||
async function tryHandleAssistantLivingChatRuntime(input) {
|
||||
const runLivingChatRuntimeSafe = input.runLivingChatRuntime ?? assistantLivingChatRuntimeAdapter_1.runAssistantLivingChatRuntime;
|
||||
const finalizeLivingChatTurnSafe = input.finalizeLivingChatTurn ?? assistantLivingChatTurnFinalizeRuntimeAdapter_1.finalizeAssistantLivingChatTurn;
|
||||
try {
|
||||
const runtime = await runLivingChatRuntimeSafe({
|
||||
userMessage: input.userMessage,
|
||||
sessionItems: input.sessionItems,
|
||||
modeDecision: input.modeDecision,
|
||||
sessionScope: input.sessionScope,
|
||||
addressRuntimeMeta: input.addressRuntimeMeta,
|
||||
traceIdFactory: input.traceIdFactory,
|
||||
toNonEmptyString: input.toNonEmptyString,
|
||||
mergeKnownOrganizations: input.mergeKnownOrganizations,
|
||||
hasAssistantDataScopeMetaQuestionSignal: input.hasAssistantDataScopeMetaQuestionSignal,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery: input.shouldHandleAsAssistantCapabilityMetaQuery,
|
||||
hasDestructiveDataActionSignal: input.hasDestructiveDataActionSignal,
|
||||
hasDangerOrCoercionSignal: input.hasDangerOrCoercionSignal,
|
||||
hasOperationalAdminActionRequestSignal: input.hasOperationalAdminActionRequestSignal,
|
||||
hasOrganizationFactLookupSignal: input.hasOrganizationFactLookupSignal,
|
||||
hasOrganizationFactFollowupSignal: input.hasOrganizationFactFollowupSignal,
|
||||
shouldEmitOrganizationSelectionReply: input.shouldEmitOrganizationSelectionReply,
|
||||
hasAssistantCapabilityQuestionSignal: input.hasAssistantCapabilityQuestionSignal,
|
||||
resolveDataScopeProbe: input.resolveDataScopeProbe,
|
||||
executeLlmChat: input.executeLlmChat,
|
||||
applyScriptGuard: input.applyScriptGuard,
|
||||
applyGroundingGuard: input.applyGroundingGuard,
|
||||
buildAssistantSafetyRefusalReply: input.buildAssistantSafetyRefusalReply,
|
||||
buildAssistantDataScopeContractReply: input.buildAssistantDataScopeContractReply,
|
||||
buildAssistantOrganizationFactBoundaryReply: input.buildAssistantOrganizationFactBoundaryReply,
|
||||
buildAssistantDataScopeSelectionReply: input.buildAssistantDataScopeSelectionReply,
|
||||
buildAssistantOperationalBoundaryReply: input.buildAssistantOperationalBoundaryReply,
|
||||
buildAssistantCapabilityContractReply: input.buildAssistantCapabilityContractReply
|
||||
});
|
||||
if (!runtime.handled || !runtime.debug) {
|
||||
return null;
|
||||
}
|
||||
const finalization = finalizeLivingChatTurnSafe({
|
||||
sessionId: input.sessionId,
|
||||
userMessage: input.userMessage,
|
||||
assistantReply: runtime.chatText,
|
||||
replyType: "factual_with_explanation",
|
||||
debug: runtime.debug,
|
||||
modeDecision: input.modeDecision,
|
||||
appendItem: input.appendItem,
|
||||
getSession: input.getSession,
|
||||
persistSession: input.persistSession,
|
||||
cloneConversation: input.cloneConversation,
|
||||
logEvent: input.logEvent,
|
||||
messageIdFactory: input.messageIdFactory
|
||||
});
|
||||
return finalization.response;
|
||||
}
|
||||
catch (error) {
|
||||
input.logEvent({
|
||||
timestamp: input.nowIso(),
|
||||
level: "warn",
|
||||
service: "assistant_loop",
|
||||
message: "assistant_living_chat_failed_fallback_to_deep",
|
||||
sessionId: input.sessionId,
|
||||
details: {
|
||||
session_id: input.sessionId,
|
||||
user_message: input.userMessage,
|
||||
reason: error instanceof Error ? error.message : String(error)
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+68
-98
@@ -79,8 +79,7 @@ const assistantDeepTurnNormalizationRuntimeAdapter_1 = __importStar(require("./a
|
||||
const assistantDeepTurnResponseRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnResponseRuntimeAdapter"));
|
||||
const assistantDeepTurnRetrievalRuntimeAdapter_1 = __importStar(require("./assistantDeepTurnRetrievalRuntimeAdapter"));
|
||||
const assistantAddressRuntimeAdapter_1 = __importStar(require("./assistantAddressRuntimeAdapter"));
|
||||
const assistantLivingChatTurnFinalizeRuntimeAdapter_1 = __importStar(require("./assistantLivingChatTurnFinalizeRuntimeAdapter"));
|
||||
const assistantLivingChatRuntimeAdapter_1 = __importStar(require("./assistantLivingChatRuntimeAdapter"));
|
||||
const assistantLivingChatHandlerRuntimeAdapter_1 = __importStar(require("./assistantLivingChatHandlerRuntimeAdapter"));
|
||||
const assistantQueryPlanning_1 = __importStar(require("./assistantQueryPlanning"));
|
||||
const iconv_lite_1 = __importDefault(require("iconv-lite"));
|
||||
const DATA_SCOPE_CACHE_TTL_MS = 60_000;
|
||||
@@ -4454,102 +4453,73 @@ class AssistantService {
|
||||
return runtime.response;
|
||||
};
|
||||
const tryHandleLivingChat = async (modeDecision, addressRuntimeMeta = null) => {
|
||||
try {
|
||||
const runtime = await (0, assistantLivingChatRuntimeAdapter_1.runAssistantLivingChatRuntime)({
|
||||
userMessage,
|
||||
sessionItems: session.items,
|
||||
modeDecision,
|
||||
sessionScope: {
|
||||
knownOrganizations: sessionOrganizationScope.knownOrganizations,
|
||||
selectedOrganization: sessionOrganizationScope.selectedOrganization,
|
||||
activeOrganization: sessionOrganizationScope.activeOrganization
|
||||
},
|
||||
addressRuntimeMeta,
|
||||
traceIdFactory: () => `chat-${(0, nanoid_1.nanoid)(10)}`,
|
||||
toNonEmptyString,
|
||||
mergeKnownOrganizations,
|
||||
hasAssistantDataScopeMetaQuestionSignal,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery,
|
||||
hasDestructiveDataActionSignal,
|
||||
hasDangerOrCoercionSignal,
|
||||
hasOperationalAdminActionRequestSignal,
|
||||
hasOrganizationFactLookupSignal,
|
||||
hasOrganizationFactFollowupSignal,
|
||||
shouldEmitOrganizationSelectionReply,
|
||||
hasAssistantCapabilityQuestionSignal,
|
||||
resolveDataScopeProbe: () => resolveAssistantDataScopeProbe(),
|
||||
executeLlmChat: async () => {
|
||||
const conversationWindow = buildLivingChatContextWindow(session.items);
|
||||
const userPrompt = buildLivingChatPrompt(userMessage, conversationWindow);
|
||||
const canonExcerpt = (0, assistantCanon_1.loadAssistantCanonExcerpt)(520);
|
||||
const chatResponse = await this.chatClient.chat({
|
||||
llmProvider: payload.llmProvider,
|
||||
apiKey: String(payload.apiKey ?? process.env.OPENAI_API_KEY ?? ''),
|
||||
model: String(payload.model ?? config_1.DEFAULT_MODEL),
|
||||
baseUrl: payload.baseUrl ?? config_1.DEFAULT_OPENAI_BASE_URL,
|
||||
temperature: payload.temperature ?? 0.35,
|
||||
maxOutputTokens: Math.max(120, Math.min(Number(payload.maxOutputTokens ?? 420), 900))
|
||||
}, {
|
||||
systemPrompt: [
|
||||
'Ты живой русскоязычный ассистент для чтения и анализа данных 1С.',
|
||||
'Работай честно: не заявляй действия, которые недоступны в этом рантайме.',
|
||||
'Разрешено: анализ и объяснение данных, формулировка запросов, подсказки по следующему шагу.',
|
||||
'Запрещено: обещать настройку 1С, админ-действия, создание/проведение документов или любые изменения в базе.',
|
||||
'Если пользователь спрашивает про возможности, отвечай только по этому контракту.',
|
||||
`Канон поведения: ${canonExcerpt}`
|
||||
].join(' '),
|
||||
developerPrompt: 'Формат: коротко и по сути, без JSON и без служебных блоков. Пиши человеко-понятно.',
|
||||
userMessage: userPrompt,
|
||||
maxOutputTokens: Math.max(120, Math.min(Number(payload.maxOutputTokens ?? 420), 900)),
|
||||
temperature: payload.temperature ?? 0.35
|
||||
});
|
||||
return sanitizeOutgoingAssistantText(chatResponse?.outputText ?? '', 'Понял. Сформулируйте, что именно нужно по данным 1С, и я помогу по шагам.');
|
||||
},
|
||||
applyScriptGuard: (chatText, runtimeUserMessage) => applyLivingChatScriptGuard(chatText, runtimeUserMessage),
|
||||
applyGroundingGuard: (guardInput) => applyLivingChatGroundingGuard(guardInput),
|
||||
buildAssistantSafetyRefusalReply,
|
||||
buildAssistantDataScopeContractReply,
|
||||
buildAssistantOrganizationFactBoundaryReply,
|
||||
buildAssistantDataScopeSelectionReply,
|
||||
buildAssistantOperationalBoundaryReply,
|
||||
buildAssistantCapabilityContractReply
|
||||
});
|
||||
if (!runtime.handled || !runtime.debug) {
|
||||
return null;
|
||||
}
|
||||
const chatText = runtime.chatText;
|
||||
const debug = runtime.debug;
|
||||
const finalization = (0, assistantLivingChatTurnFinalizeRuntimeAdapter_1.finalizeAssistantLivingChatTurn)({
|
||||
sessionId,
|
||||
userMessage,
|
||||
assistantReply: chatText,
|
||||
replyType: 'factual_with_explanation',
|
||||
debug,
|
||||
modeDecision,
|
||||
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;
|
||||
}
|
||||
catch (error) {
|
||||
(0, log_1.logJson)({
|
||||
timestamp: new Date().toISOString(),
|
||||
level: 'warn',
|
||||
service: 'assistant_loop',
|
||||
message: 'assistant_living_chat_failed_fallback_to_deep',
|
||||
sessionId,
|
||||
details: {
|
||||
session_id: sessionId,
|
||||
user_message: userMessage,
|
||||
reason: error instanceof Error ? error.message : String(error)
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
return (0, assistantLivingChatHandlerRuntimeAdapter_1.tryHandleAssistantLivingChatRuntime)({
|
||||
sessionId,
|
||||
userMessage,
|
||||
sessionItems: session.items,
|
||||
modeDecision,
|
||||
sessionScope: {
|
||||
knownOrganizations: sessionOrganizationScope.knownOrganizations,
|
||||
selectedOrganization: sessionOrganizationScope.selectedOrganization,
|
||||
activeOrganization: sessionOrganizationScope.activeOrganization
|
||||
},
|
||||
addressRuntimeMeta,
|
||||
traceIdFactory: () => `chat-${(0, nanoid_1.nanoid)(10)}`,
|
||||
toNonEmptyString,
|
||||
mergeKnownOrganizations,
|
||||
hasAssistantDataScopeMetaQuestionSignal,
|
||||
shouldHandleAsAssistantCapabilityMetaQuery,
|
||||
hasDestructiveDataActionSignal,
|
||||
hasDangerOrCoercionSignal,
|
||||
hasOperationalAdminActionRequestSignal,
|
||||
hasOrganizationFactLookupSignal,
|
||||
hasOrganizationFactFollowupSignal,
|
||||
shouldEmitOrganizationSelectionReply,
|
||||
hasAssistantCapabilityQuestionSignal,
|
||||
resolveDataScopeProbe: () => resolveAssistantDataScopeProbe(),
|
||||
executeLlmChat: async () => {
|
||||
const conversationWindow = buildLivingChatContextWindow(session.items);
|
||||
const userPrompt = buildLivingChatPrompt(userMessage, conversationWindow);
|
||||
const canonExcerpt = (0, assistantCanon_1.loadAssistantCanonExcerpt)(520);
|
||||
const chatResponse = await this.chatClient.chat({
|
||||
llmProvider: payload.llmProvider,
|
||||
apiKey: String(payload.apiKey ?? process.env.OPENAI_API_KEY ?? ''),
|
||||
model: String(payload.model ?? config_1.DEFAULT_MODEL),
|
||||
baseUrl: payload.baseUrl ?? config_1.DEFAULT_OPENAI_BASE_URL,
|
||||
temperature: payload.temperature ?? 0.35,
|
||||
maxOutputTokens: Math.max(120, Math.min(Number(payload.maxOutputTokens ?? 420), 900))
|
||||
}, {
|
||||
systemPrompt: [
|
||||
'Ты живой русскоязычный ассистент для чтения и анализа данных 1С.',
|
||||
'Работай честно: не заявляй действия, которые недоступны в этом рантайме.',
|
||||
'Разрешено: анализ и объяснение данных, формулировка запросов, подсказки по следующему шагу.',
|
||||
'Запрещено: обещать настройку 1С, админ-действия, создание/проведение документов или любые изменения в базе.',
|
||||
'Если пользователь спрашивает про возможности, отвечай только по этому контракту.',
|
||||
`Канон поведения: ${canonExcerpt}`
|
||||
].join(' '),
|
||||
developerPrompt: 'Формат: коротко и по сути, без JSON и без служебных блоков. Пиши человеко-понятно.',
|
||||
userMessage: userPrompt,
|
||||
maxOutputTokens: Math.max(120, Math.min(Number(payload.maxOutputTokens ?? 420), 900)),
|
||||
temperature: payload.temperature ?? 0.35
|
||||
});
|
||||
return sanitizeOutgoingAssistantText(chatResponse?.outputText ?? '', 'Понял. Сформулируйте, что именно нужно по данным 1С, и я помогу по шагам.');
|
||||
},
|
||||
applyScriptGuard: (chatText, runtimeUserMessage) => applyLivingChatScriptGuard(chatText, runtimeUserMessage),
|
||||
applyGroundingGuard: (guardInput) => applyLivingChatGroundingGuard(guardInput),
|
||||
buildAssistantSafetyRefusalReply,
|
||||
buildAssistantDataScopeContractReply,
|
||||
buildAssistantOrganizationFactBoundaryReply,
|
||||
buildAssistantDataScopeSelectionReply,
|
||||
buildAssistantOperationalBoundaryReply,
|
||||
buildAssistantCapabilityContractReply,
|
||||
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)}`,
|
||||
nowIso: () => new Date().toISOString()
|
||||
});
|
||||
};
|
||||
let addressRuntimeMetaForDeep = null;
|
||||
const runAddressLaneAttempt = async (messageUsed, carryMeta, analysisDateHint) => {
|
||||
|
||||
Reference in New Issue
Block a user