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

183 lines
8.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadCapabilitiesRegistry = loadCapabilitiesRegistry;
exports.buildCapabilityContractReplyFromRegistry = buildCapabilityContractReplyFromRegistry;
exports.resolveNearestCapabilityGroup = resolveNearestCapabilityGroup;
const fs_1 = __importDefault(require("fs"));
const config_1 = require("../config");
const FALLBACK_REGISTRY = {
schema_version: "capabilities_registry_fallback_v1",
updated_at: "2026-04-09T00:00:00.000Z",
assistant_mode: "read_only",
groups: [
{
group_code: "vat",
group_title: "НДС",
description: "Срезы и расчеты НДС на базе данных 1С.",
risk_level: "high",
maturity_status: "partial",
supported_operations: ["vat_period_snapshot", "vat_payable_forecast"],
unsupported_operations: ["submit_tax_declaration"],
required_entities: ["period", "organization"],
optional_entities: ["counterparty"],
typical_queries: ["Сколько НДС к уплате за период?"],
related_routes: [],
safe_alternatives: ["Показать движения по 68/19 за период"],
one_c_hints: ["РегистрБухгалтерии.Хозрасчетный"]
},
{
group_code: "counterparties",
group_title: "Контрагенты",
description: "Документы, операции, договоры и срезы по контрагентам.",
risk_level: "medium",
maturity_status: "production_ready",
supported_operations: ["list_documents_by_counterparty", "list_contracts_by_counterparty"],
unsupported_operations: ["edit_counterparty_card"],
required_entities: ["counterparty_scope_or_contract"],
optional_entities: ["period", "organization"],
typical_queries: ["Покажи документы по контрагенту"],
related_routes: [],
safe_alternatives: ["Уточнить ИНН/наименование контрагента"],
one_c_hints: ["Справочник.Контрагенты"]
},
{
group_code: "boundaries",
group_title: "Ограничения",
description: "Операции, которые ассистент не выполняет.",
risk_level: "high",
maturity_status: "production_ready",
supported_operations: ["explain_boundary", "suggest_safe_next_step"],
unsupported_operations: ["configure_1c", "admin_server_actions", "create_or_post_documents"],
required_entities: [],
optional_entities: [],
typical_queries: ["Можешь настроить 1С?"],
related_routes: [],
safe_alternatives: ["Сформировать план диагностики для 1С/ИТ-админа"],
one_c_hints: []
}
]
};
let cache = null;
function toRecord(value) {
if (!value || typeof value !== "object" || Array.isArray(value))
return null;
return value;
}
function toStringSafe(value) {
if (typeof value !== "string")
return null;
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : null;
}
function toArray(value) {
return Array.isArray(value) ? value : [];
}
function readRegistryFromFile() {
if (!fs_1.default.existsSync(config_1.ASSISTANT_CAPABILITIES_REGISTRY_FILE))
return null;
try {
const raw = fs_1.default.readFileSync(config_1.ASSISTANT_CAPABILITIES_REGISTRY_FILE, "utf-8");
const parsed = JSON.parse(raw);
const root = toRecord(parsed);
if (!root)
return null;
const groups = toArray(root.groups)
.map((item) => toRecord(item))
.filter((item) => item !== null)
.map((item) => ({
group_code: toStringSafe(item.group_code) ?? "unknown_group",
group_title: toStringSafe(item.group_title) ?? "Группа",
description: toStringSafe(item.description) ?? "",
risk_level: toStringSafe(item.risk_level) ?? "medium",
maturity_status: toStringSafe(item.maturity_status) ??
"partial",
supported_operations: toArray(item.supported_operations)
.map((v) => toStringSafe(v))
.filter((v) => v !== null),
unsupported_operations: toArray(item.unsupported_operations)
.map((v) => toStringSafe(v))
.filter((v) => v !== null),
required_entities: toArray(item.required_entities)
.map((v) => toStringSafe(v))
.filter((v) => v !== null),
optional_entities: toArray(item.optional_entities)
.map((v) => toStringSafe(v))
.filter((v) => v !== null),
typical_queries: toArray(item.typical_queries)
.map((v) => toStringSafe(v))
.filter((v) => v !== null),
related_routes: toArray(item.related_routes)
.map((v) => toStringSafe(v))
.filter((v) => v !== null),
safe_alternatives: toArray(item.safe_alternatives)
.map((v) => toStringSafe(v))
.filter((v) => v !== null),
one_c_hints: toArray(item.one_c_hints)
.map((v) => toStringSafe(v))
.filter((v) => v !== null)
}));
if (groups.length === 0)
return null;
return {
schema_version: toStringSafe(root.schema_version) ?? "capabilities_registry_v1",
updated_at: toStringSafe(root.updated_at) ?? new Date().toISOString(),
assistant_mode: toStringSafe(root.assistant_mode) ?? "read_only",
groups
};
}
catch {
return null;
}
}
function loadCapabilitiesRegistry() {
try {
const mtimeMs = fs_1.default.existsSync(config_1.ASSISTANT_CAPABILITIES_REGISTRY_FILE)
? fs_1.default.statSync(config_1.ASSISTANT_CAPABILITIES_REGISTRY_FILE).mtimeMs
: -1;
if (cache && cache.mtimeMs === mtimeMs) {
return cache.value;
}
const value = readRegistryFromFile() ?? FALLBACK_REGISTRY;
cache = { mtimeMs, value };
return value;
}
catch {
return cache?.value ?? FALLBACK_REGISTRY;
}
}
function buildCapabilityContractReplyFromRegistry() {
const registry = loadCapabilitiesRegistry();
const topGroups = registry.groups.slice(0, 6);
const groupLines = topGroups.map((group, index) => {
const ops = group.supported_operations.slice(0, 3).join(", ");
return `${index + 1}. ${group.group_title}: ${group.description}${ops ? ` (например: ${ops})` : ""}.`;
});
return [
"Я ассистент по анализу данных 1С в режиме чтения.",
"Что умею по группам:",
...groupLines,
"Если хотите, раскрою любую группу точечно и дам готовую формулировку запроса.",
"Что не делаю: не настраиваю 1С, не меняю конфигурацию, не создаю и не провожу документы, не выполняю админ-действия на сервере."
].join("\n");
}
function resolveNearestCapabilityGroup(input) {
const registry = loadCapabilitiesRegistry();
const haystack = `${String(input.domain ?? "")} ${String(input.queryClass ?? "")}`.toLowerCase();
if (!haystack.trim())
return null;
const scoring = registry.groups.map((group) => {
let score = 0;
const bucket = `${group.group_code} ${group.group_title} ${group.description} ${group.supported_operations.join(" ")}`.toLowerCase();
for (const token of haystack.split(/[\s._/-]+/g).filter(Boolean)) {
if (bucket.includes(token))
score += 1;
}
return { group, score };
});
scoring.sort((a, b) => b.score - a.score);
return scoring[0] && scoring[0].score > 0 ? scoring[0].group : null;
}