NODEDC_1C/llm_normalizer/backend/dist/services/assistantMcpDiscoveryRespon...

177 lines
8.2 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";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ASSISTANT_MCP_DISCOVERY_RESPONSE_CANDIDATE_SCHEMA_VERSION = void 0;
exports.buildAssistantMcpDiscoveryResponseCandidate = buildAssistantMcpDiscoveryResponseCandidate;
exports.ASSISTANT_MCP_DISCOVERY_RESPONSE_CANDIDATE_SCHEMA_VERSION = "assistant_mcp_discovery_response_candidate_v1";
function toRecordObject(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return null;
}
return value;
}
function toNonEmptyString(value) {
if (value === null || value === undefined) {
return null;
}
const text = String(value).trim();
return text.length > 0 ? text : null;
}
function toStringList(value) {
if (!Array.isArray(value)) {
return [];
}
return value.map((item) => toNonEmptyString(item)).filter((item) => Boolean(item));
}
function normalizeReasonCode(value) {
const normalized = value
.trim()
.replace(/[^\p{L}\p{N}_.:-]+/gu, "_")
.replace(/^_+|_+$/g, "")
.toLowerCase();
return normalized.length > 0 ? normalized.slice(0, 120) : null;
}
function pushReason(target, value) {
const normalized = normalizeReasonCode(value);
if (normalized && !target.includes(normalized)) {
target.push(normalized);
}
}
function uniqueStrings(values) {
const result = [];
for (const value of values) {
const text = String(value ?? "").trim();
if (text && !result.includes(text)) {
result.push(text);
}
}
return result;
}
function hasInternalMechanics(value) {
const text = value.toLowerCase();
return (text.includes("query_documents") ||
text.includes("query_movements") ||
text.includes("primitive") ||
text.includes("pilot_") ||
text.includes("runtime_") ||
text.includes("planner_") ||
text.includes("catalog_") ||
text.includes("select "));
}
function userFacingLines(values) {
return uniqueStrings(values).filter((line) => !hasInternalMechanics(line));
}
function localizeLine(value) {
const counterpartyMatch = value.match(/^1C activity rows were found for counterparty\s+(.+)$/i);
if (counterpartyMatch) {
return `В 1С найдены строки активности по контрагенту ${counterpartyMatch[1]}.`;
}
if (/^1C activity rows were found for the requested counterparty scope$/i.test(value)) {
return "В 1С найдены строки активности по запрошенному контрагентскому контуру.";
}
const valueFlowMatch = value.match(/^1C value-flow rows were found for counterparty\s+(.+)$/i);
if (valueFlowMatch) {
return `В 1С найдены строки денежных движений по контрагенту ${valueFlowMatch[1]}.`;
}
if (/^1C value-flow rows were found for the requested counterparty scope$/i.test(value)) {
return "В 1С найдены строки денежных движений по запрошенному контрагентскому контуру.";
}
if (/^Business activity duration may be inferred from first and latest confirmed 1C activity rows$/i.test(value)) {
return "Длительность деловой активности можно оценивать только как вывод по первой и последней подтвержденной строке активности в 1С.";
}
if (/^Counterparty value-flow total was calculated from confirmed 1C movement rows$/i.test(value)) {
return "Сумма рассчитана только по подтвержденным строкам денежных движений в 1С.";
}
if (/^Legal registration date is not proven by this MCP discovery pilot$/i.test(value)) {
return "Юридическая дата регистрации этим поиском не подтверждена.";
}
if (/^Full turnover outside the checked period is not proven by this MCP discovery pilot$/i.test(value)) {
return "Полный оборот вне проверенного периода этим поиском не подтвержден.";
}
if (/^Full all-time turnover is not proven without an explicit checked period$/i.test(value)) {
return "Полный оборот за все время без явно проверенного периода не подтвержден.";
}
return value;
}
function section(title, lines) {
const clean = userFacingLines(lines.map(localizeLine));
if (clean.length === 0) {
return null;
}
return `${title}\n${clean.map((line) => `- ${line}`).join("\n")}`;
}
function statusFrom(entryPoint) {
if (!entryPoint || entryPoint.entry_status === "skipped_not_applicable") {
return "not_applicable";
}
if (entryPoint.entry_status === "skipped_needs_more_context") {
return "clarification_candidate";
}
const bridgeStatus = toNonEmptyString(toRecordObject(entryPoint.bridge)?.bridge_status);
if (bridgeStatus === "answer_draft_ready") {
return "ready_for_guarded_use";
}
if (bridgeStatus === "needs_clarification") {
return "clarification_candidate";
}
if (bridgeStatus === "checked_sources_only") {
return "checked_sources_only_candidate";
}
if (bridgeStatus === "blocked") {
return "blocked";
}
if (bridgeStatus === "unsupported") {
return "unsupported";
}
return "not_applicable";
}
function replyTypeFor(status) {
if (status === "clarification_candidate") {
return "clarification_required";
}
if (status === "blocked" || status === "unsupported" || status === "not_applicable") {
return "no_grounded_answer";
}
return "partial_coverage";
}
function buildReplyText(entryPoint, status) {
const bridge = toRecordObject(entryPoint.bridge);
const draft = toRecordObject(bridge?.answer_draft);
if (!draft) {
if (status === "clarification_candidate") {
return "Нужно уточнить контекст перед поиском в 1С: контрагента, период или организацию.";
}
return null;
}
const blocks = [
toNonEmptyString(draft.headline) ? `Коротко: ${localizeLine(String(draft.headline))}` : null,
section("Что подтверждено:", toStringList(draft.confirmed_lines)),
section("Что можно сказать только как вывод:", toStringList(draft.inference_lines)),
section("Что не подтверждено:", toStringList(draft.unknown_lines)),
section("Ограничения проверки:", toStringList(draft.limitation_lines)),
toNonEmptyString(draft.next_step_line) ? `Следующий шаг: ${localizeLine(String(draft.next_step_line))}` : null
].filter((item) => Boolean(item));
const reply = blocks.join("\n\n").trim();
return reply.length > 0 && !hasInternalMechanics(reply) ? reply : null;
}
function buildAssistantMcpDiscoveryResponseCandidate(entryPoint) {
const entry = entryPoint ?? null;
const status = statusFrom(entry);
const reasonCodes = uniqueStrings(entry?.reason_codes ?? []);
pushReason(reasonCodes, `mcp_discovery_response_candidate_${status}`);
pushReason(reasonCodes, "mcp_discovery_response_candidate_not_hot_wired");
const replyText = entry && (status === "ready_for_guarded_use" || status === "checked_sources_only_candidate" || status === "clarification_candidate")
? buildReplyText(entry, status)
: null;
return {
schema_version: exports.ASSISTANT_MCP_DISCOVERY_RESPONSE_CANDIDATE_SCHEMA_VERSION,
policy_owner: "assistantMcpDiscoveryResponseCandidate",
candidate_status: replyText ? status : status === "clarification_candidate" ? status : status,
hot_runtime_wired: false,
reply_type: replyTypeFor(status),
reply_text: replyText,
eligible_for_future_hot_runtime: Boolean(replyText) && (status === "ready_for_guarded_use" || status === "checked_sources_only_candidate" || status === "clarification_candidate"),
must_keep_internal_mechanics_hidden: true,
reason_codes: reasonCodes
};
}