ARCH: добавить candidate ответа MCP discovery
This commit is contained in:
+159
@@ -0,0 +1,159 @@
|
||||
"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("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С найдены строки активности по запрошенному контрагентскому контуру.";
|
||||
}
|
||||
if (/^Business activity duration may be inferred from first and latest confirmed 1C activity rows$/i.test(value)) {
|
||||
return "Длительность деловой активности можно оценивать только как вывод по первой и последней подтвержденной строке активности в 1С.";
|
||||
}
|
||||
if (/^Legal registration date is not proven by this MCP discovery pilot$/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
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user