"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AssistantSessionLogger = void 0; const path_1 = __importDefault(require("path")); const config_1 = require("../config"); const files_1 = require("../utils/files"); function unique(values) { return Array.from(new Set(values.filter((item) => typeof item === "string" && item.length > 0))); } function toObject(value) { if (!value || typeof value !== "object" || Array.isArray(value)) { return null; } return value; } function toStringOrNull(value) { if (typeof value !== "string") { return null; } const trimmed = value.trim(); return trimmed ? trimmed : null; } function extractFragments(assistantItem) { if (!assistantItem.debug || !Array.isArray(assistantItem.debug.fragments)) { return []; } return assistantItem.debug.fragments .map((item) => toObject(item)) .filter((item) => item !== null); } function extractNormalizedQuestion(userText, assistantItem) { const normalized = toObject(assistantItem.debug?.normalized); if (normalized) { const fromUserMessageRaw = toStringOrNull(normalized.user_message_raw); if (fromUserMessageRaw) return fromUserMessageRaw; const fromUserQuestionRaw = toStringOrNull(normalized.user_question_raw); if (fromUserQuestionRaw) return fromUserQuestionRaw; const fromNormalizedQuestion = toStringOrNull(normalized.normalized_question); if (fromNormalizedQuestion) return fromNormalizedQuestion; } const fragments = extractFragments(assistantItem); if (fragments.length > 0) { const joined = fragments .map((fragment) => toStringOrNull(fragment.normalized_fragment_text) ?? toStringOrNull(fragment.raw_fragment_text)) .filter((item) => Boolean(item)) .join(" | "); if (joined) { return joined; } } return userText; } function buildRouteLookup(assistantItem) { const output = new Map(); if (!assistantItem.debug || !Array.isArray(assistantItem.debug.routes)) { return output; } for (const route of assistantItem.debug.routes) { const routeObject = toObject(route); if (!routeObject) continue; const fragmentId = toStringOrNull(routeObject.fragment_id); if (!fragmentId) continue; output.set(fragmentId, routeObject); } return output; } function buildDecompositionLines(assistantItem) { const fragments = extractFragments(assistantItem); if (fragments.length === 0) { return ["Фрагменты декомпозиции не выделены."]; } const routeLookup = buildRouteLookup(assistantItem); return fragments.map((fragment, index) => { const fragmentId = toStringOrNull(fragment.fragment_id) ?? `F${index + 1}`; const fragmentText = toStringOrNull(fragment.normalized_fragment_text) ?? toStringOrNull(fragment.raw_fragment_text) ?? "текст фрагмента отсутствует"; const executionReadiness = toStringOrNull(fragment.execution_readiness); const routeStatus = toStringOrNull(fragment.route_status); const routeObject = routeLookup.get(fragmentId); const route = toStringOrNull(routeObject?.route); const noRouteReason = toStringOrNull(fragment.no_route_reason) ?? toStringOrNull(routeObject?.no_route_reason); const parts = [`${fragmentId}: ${fragmentText}`]; if (executionReadiness) parts.push(`execution_readiness=${executionReadiness}`); if (routeStatus) parts.push(`route_status=${routeStatus}`); if (route) parts.push(`route=${route}`); if (noRouteReason) parts.push(`no_route_reason=${noRouteReason}`); return parts.join("; "); }); } function toHumanBlock(input) { const lines = []; lines.push(`Вопрос: ${input.questionRaw}`); lines.push(`Понято как: ${input.questionUnderstood}`); lines.push("Декомпозиция:"); lines.push(...input.decomposition.map((item) => `- ${item}`)); lines.push(`Ответ: ${input.answer}`); return lines.join("\n"); } function buildTurns(items) { const turns = []; const pendingUsers = []; for (const item of items) { if (item.role === "user") { pendingUsers.push(item); continue; } const pairedUser = pendingUsers.shift(); if (!pairedUser) { continue; } const questionRaw = pairedUser.text; const questionUnderstood = extractNormalizedQuestion(questionRaw, item); const decomposition = buildDecompositionLines(item); const answer = item.text; turns.push({ turn_id: `turn-${turns.length + 1}`, started_at: pairedUser.created_at ?? null, completed_at: item.created_at ?? null, human_block: toHumanBlock({ questionRaw, questionUnderstood, decomposition, answer }), human_readable: { question_raw: questionRaw, question_understood: questionUnderstood, decomposition, answer, reply_type: item.reply_type }, technical_json: { trace_id: item.trace_id, user_message: pairedUser, assistant_message: item, debug: item.debug } }); } return turns; } class AssistantSessionLogger { rootDir; constructor(rootDir = config_1.ASSISTANT_SESSIONS_DIR) { this.rootDir = rootDir; } persistSession(session) { try { (0, files_1.ensureDir)(this.rootDir); const filePath = path_1.default.resolve(this.rootDir, `${session.session_id}.json`); const startedAt = session.items[0]?.created_at ?? session.updated_at; const userMessages = session.items.filter((item) => item.role === "user").length; const assistantMessages = session.items.filter((item) => item.role === "assistant").length; const assistantItems = session.items.filter((item) => item.role === "assistant"); const lastAssistant = assistantItems.length > 0 ? assistantItems[assistantItems.length - 1] : null; const traceIds = unique(session.items.map((item) => item.trace_id)); const replyTypes = Array.from(new Set(session.items .map((item) => item.reply_type) .filter((item) => typeof item === "string" && item.length > 0))); const turns = buildTurns(session.items); const record = { schema_version: "assistant_session_log_v1", session_id: session.session_id, started_at: startedAt, updated_at: session.updated_at, counters: { total_messages: session.items.length, user_messages: userMessages, assistant_messages: assistantMessages }, trace_ids: traceIds, reply_types: replyTypes, investigation_state: session.investigation_state, turns, conversation: session.items, last_assistant: { message_id: lastAssistant?.message_id ?? null, reply_type: lastAssistant?.reply_type ?? null, trace_id: lastAssistant?.trace_id ?? null, created_at: lastAssistant?.created_at ?? null } }; (0, files_1.writeJsonFile)(filePath, record); } catch (error) { const code = error?.code; if (code === "ENOSPC") { return; } throw error; } } } exports.AssistantSessionLogger = AssistantSessionLogger;