NODEDC_1C/llm_normalizer/backend/dist/services/addressMcpClient.js

225 lines
7.0 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeAddressMcpQuery = executeAddressMcpQuery;
const config_1 = require("../config");
function toStringValue(value) {
if (value === null || value === undefined) {
return "";
}
return String(value);
}
function parseFiniteNumber(value) {
if (typeof value === "number" && Number.isFinite(value)) {
return value;
}
if (typeof value === "string") {
const parsed = Number(value.replace(",", ".").trim());
if (Number.isFinite(parsed)) {
return parsed;
}
}
return null;
}
function parseRowsFromTextTable(source) {
const normalized = String(source ?? "").replace(/\r/g, "").trim();
if (!normalized) {
return [];
}
const headerMatch = normalized.match(/\{([^}]*)\}:/);
if (!headerMatch) {
return [];
}
const columns = String(headerMatch[1] ?? "")
.split(",")
.map((item) => item.replace(/^"+|"+$/g, "").trim())
.filter(Boolean);
const body = normalized.slice((headerMatch.index ?? 0) + headerMatch[0].length).trim();
if (!body) {
return [];
}
const rows = [];
const parseCsvLine = (line) => {
const values = [];
let current = "";
let inQuotes = false;
for (let index = 0; index < line.length; index += 1) {
const char = line[index];
if (char === '"') {
if (inQuotes && line[index + 1] === '"') {
current += '"';
index += 1;
continue;
}
inQuotes = !inQuotes;
continue;
}
if (char === "," && !inQuotes) {
values.push(current.trim());
current = "";
continue;
}
current += char;
}
values.push(current.trim());
return values;
};
const lines = body
.split("\n")
.map((line) => line.trim())
.filter(Boolean);
for (const line of lines) {
const values = parseCsvLine(line);
if (values.length === 0) {
continue;
}
const row = {};
for (let index = 0; index < columns.length; index += 1) {
const key = columns[index] ?? `column_${index + 1}`;
const raw = values[index] ?? "";
const parsed = parseFiniteNumber(raw);
row[key] = parsed ?? raw;
}
if (values[0])
row.Period = values[0];
if (values[1])
row.Registrator = values[1];
if (values[2])
row.AccountDt = values[2];
if (values[3])
row.AccountKt = values[3];
if (values[4])
row.Amount = parseFiniteNumber(values[4]) ?? values[4];
rows.push(row);
}
return rows;
}
function parseExecutePayload(payload) {
if (!payload || typeof payload !== "object") {
return {
ok: false,
rows: [],
error: "MCP payload is empty or malformed"
};
}
const source = payload;
if (source.success !== true) {
return {
ok: false,
rows: [],
error: toStringValue(source.error).trim() || "MCP execute_query returned success=false"
};
}
if (Array.isArray(source.data)) {
const rows = source.data
.map((item) => (item && typeof item === "object" ? item : null))
.filter((item) => item !== null);
return {
ok: true,
rows,
error: null
};
}
if (typeof source.data === "string") {
return {
ok: true,
rows: parseRowsFromTextTable(source.data),
error: null
};
}
if (source.data && typeof source.data === "object" && Array.isArray(source.data.rows)) {
const rows = (source.data.rows ?? [])
.map((item) => (item && typeof item === "object" ? item : null))
.filter((item) => item !== null);
return {
ok: true,
rows,
error: null
};
}
return {
ok: true,
rows: [],
error: null
};
}
function buildMcpUrl(endpoint) {
const normalizedEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`;
const separator = normalizedEndpoint.includes("?") ? "&" : "?";
return `${config_1.ASSISTANT_MCP_PROXY_URL}${normalizedEndpoint}${separator}channel=${encodeURIComponent(config_1.ASSISTANT_MCP_CHANNEL)}`;
}
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function filterRowsByAccountScope(rows, accountScope) {
if (accountScope.length === 0) {
return rows;
}
const matchers = accountScope.map((account) => new RegExp(`\\b${escapeRegExp(account)}(?:\\.\\d{1,2})?\\b`, "i"));
return rows.filter((row) => {
const searchable = Object.values(row)
.map((item) => String(item ?? ""))
.join(" ");
return matchers.some((matcher) => matcher.test(searchable));
});
}
async function executeAddressMcpQuery(input) {
const endpoint = buildMcpUrl("/api/execute_query");
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), Math.max(300, config_1.ASSISTANT_MCP_TIMEOUT_MS));
try {
const response = await fetch(endpoint, {
method: "POST",
headers: {
"content-type": "application/json; charset=utf-8"
},
body: JSON.stringify({
query: input.query,
limit: input.limit
}),
signal: controller.signal
});
const responseText = await response.text();
if (!response.ok) {
return {
fetched_rows: 0,
matched_rows: 0,
raw_rows: [],
rows: [],
error: `MCP HTTP ${response.status}: ${responseText.slice(0, 240)}`
};
}
const payload = responseText.trim() ? JSON.parse(responseText) : {};
const parsed = parseExecutePayload(payload);
if (!parsed.ok) {
return {
fetched_rows: 0,
matched_rows: 0,
raw_rows: [],
rows: [],
error: parsed.error
};
}
const filtered = filterRowsByAccountScope(parsed.rows, Array.isArray(input.account_scope) ? input.account_scope : []);
return {
fetched_rows: parsed.rows.length,
matched_rows: filtered.length,
raw_rows: parsed.rows,
rows: filtered,
error: null
};
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
return {
fetched_rows: 0,
matched_rows: 0,
raw_rows: [],
rows: [],
error: `MCP fetch failed: ${message}`
};
}
finally {
clearTimeout(timeout);
}
}