"use strict"; const ALEM_TIMEOUT_MS = 600000; function fetchWithTimeout(url, options, timeoutMs) { const controller = new AbortController(); const timer = setTimeout(function () { controller.abort(); }, timeoutMs); return fetch(url, Object.assign({ signal: controller.signal }, options)).finally(function () { clearTimeout(timer); }); } function createAlemSession() { const url = process.env.ALEM_BASE_URL + "/api/v1/open-api/conversation"; return fetchWithTimeout(url, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + process.env.ALEM_API_KEY }, body: JSON.stringify({ template_id: process.env.ALEM_TEMPLATE_ID, title: "KFU-agent-call", user_id: "kfu_app" }) }).then(function (resp) { if (!resp.ok) throw new Error("Alem create " + resp.status + ": " + resp.statusText); return resp.json().then(function (body) { const sid = body && body.data && body.data.session_id; if (!sid) throw new Error("Alem no session_id: " + JSON.stringify(body).slice(0, 200)); return sid; }); }); } function extractSseText(raw) { var answer = null; var lines = String(raw).split(String.fromCharCode(10)); for (var i = 0; i < lines.length; i++) { var s = lines[i].trim(); if (!s.startsWith(String.fromCharCode(100,97,116,97,58))) continue; var payload = s.slice(5).trim(); if (!payload || payload === String.fromCharCode(91,68,79,78,69,93)) continue; try { var ev = JSON.parse(payload); if (ev && ev.detail && typeof ev.detail.full_content === String.fromCharCode(115,116,114,105,110,103) && ev.detail.full_content) { answer = ev.detail.full_content; } } catch (e) { } } return answer || String.fromCharCode(78,101,116,32,101,116,32,111,116,118,101,116); } function askAlem(sessionId, message) { var url = process.env.ALEM_BASE_URL + sendPath(sessionId); return fetchWithTimeout(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + process.env.ALEM_API_KEY }, body: JSON.stringify({ content: message, user_id: 'kfu_app', model_api_key: process.env.ALEM_API_KEY }) }, ALEM_TIMEOUT_MS).then(function (resp) { if (!resp.ok) throw new Error('Alem send ' + resp.status + ': ' + resp.statusText); return resp.text().then(function (raw) { return extractSseText(raw); }); }); } function sendPath(id) { return '/api/v1/open-api/conversation/' + id + '/send'; } function extractJsonFromText(text) { var s = String(text || ''); var fenceStart = s.indexOf('```'); if (fenceStart !== -1) { var inner = s.slice(fenceStart + 3); var langEnd = inner.indexOf('```'); if (langEnd !== -1) inner = inner.slice(langEnd + 3); var jsonStart = inner.indexOf('{'); var jsonEnd = inner.lastIndexOf('}'); if (jsonStart !== -1 && jsonEnd > jsonStart) { try { return JSON.parse(inner.slice(jsonStart, jsonEnd + 1)); } catch (e) { } } } var first = s.indexOf('{'); var last = s.lastIndexOf('}'); if (first !== -1 && last > first) { try { return JSON.parse(s.slice(first, last + 1)); } catch (e) { } } return null; } function callKfuAgent(company, period, fileText) { var prompt = [ 'Расчёт коэффициентов финансовой устойчивости (КФУ).', 'Компания: ' + company, 'Отчётный период: ' + period, 'Данные отчёта:', fileText, '', 'Верни ТОЛЬКО один JSON-объект без пояснений со строгими полями:', '{company, period, units, debt, ebitda_ltm, interest_ltm, equity, k1, k2, k3, zone_k1, zone_k2, zone_k3, zone_company, flags, sources, comment}.', 'Числа — числом (без слов и валютного знака), zone_* — одно из: зелёная, жёлтая, красная, без оценки.', 'flags — массив строк (пустой, если замечаний нет), sources — объект-источник цифр, comment — краткое замечание (или пустая строка).' ].join("\n"); return createAlemSession().then(function (sid) { return askAlem(sid, prompt).then(function (text) { var data = extractJsonFromText(text); if (!data) throw new Error('Alem не вернул JSON. Ответ: ' + String(text).slice(0, 300)); return data; }); }); } module.exports = { callKfuAgent, extractSseText, extractJsonFromText };