АРЧ - UI и domain loop: синхронизировать локальную LLM-модель через shared config + КВИН 3

This commit is contained in:
2026-04-15 09:15:35 +03:00
parent bc381c012e
commit 8866176be6
24 changed files with 801 additions and 73 deletions
+2 -1
View File
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TRACES_DIR = exports.DATA_DIR = exports.VAT_PAYABLE_19_PREFIXES = exports.VAT_PAYABLE_68_PREFIXES = exports.ASSISTANT_MCP_LIVE_LIMIT = exports.ASSISTANT_MCP_TIMEOUT_MS = exports.ASSISTANT_MCP_CHANNEL = exports.ASSISTANT_MCP_PROXY_URL = exports.FEATURE_ASSISTANT_LIVING_CHAT_ROUTER_V1 = exports.FEATURE_ASSISTANT_ROUTE_EXPECTATION_HARD_GUARD_V1 = exports.FEATURE_ASSISTANT_ROUTE_EXPECTATION_AUDIT_V1 = exports.FEATURE_ASSISTANT_ROUTE_SHADOW_PAYABLES_EXACT_V1 = exports.FEATURE_ASSISTANT_ROUTE_RECEIVABLES_HEURISTIC_V1 = exports.FEATURE_ASSISTANT_ROUTE_PAYABLES_HEURISTIC_V1 = exports.FEATURE_ASSISTANT_ROUTE_RECEIVABLES_CONFIRMED_V1 = exports.FEATURE_ASSISTANT_ROUTE_PAYABLES_CONFIRMED_V1 = exports.FEATURE_ASSISTANT_ROUTE_BALANCE_EXACT_V1 = exports.FEATURE_ASSISTANT_ROUTE_DRILLDOWN_V1 = exports.FEATURE_ASSISTANT_ROUTE_ADDRESS_GENERIC_V1 = exports.FEATURE_ASSISTANT_CAPABILITY_ROUTE_GUARD_V1 = exports.FEATURE_ASSISTANT_ADDRESS_NAVIGATION_STATE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_LIVE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_LLM_PREDECOMPOSE_V1 = exports.FEATURE_ASSISTANT_ADDRESS_QUERY_V1 = exports.FEATURE_ASSISTANT_MCP_RUNTIME_V1 = exports.FEATURE_ASSISTANT_GRAPH_RUNTIME_V1 = exports.FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1 = exports.FEATURE_ASSISTANT_LIFECYCLE_RUNTIME_V1 = exports.FEATURE_ASSISTANT_STAGE2_EVAL_V1 = exports.FEATURE_ASSISTANT_PROBLEM_UNIT_CONTINUITY_V1 = exports.FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1 = exports.FEATURE_ASSISTANT_PROBLEM_UNITS_V1 = exports.FEATURE_ASSISTANT_ACCOUNTANT_EVAL_V1 = exports.FEATURE_ASSISTANT_ANSWER_POLICY_V11 = exports.FEATURE_ASSISTANT_ANTI_GENERIC_RANKING_GUARD_V1 = exports.FEATURE_ASSISTANT_MIN_EVIDENCE_GATE_V1 = exports.FEATURE_ASSISTANT_BROAD_GUARD_V1 = exports.FEATURE_ASSISTANT_EVIDENCE_ENRICHMENT_V1 = exports.FEATURE_ASSISTANT_STATE_FOLLOWUP_BINDING_V1 = exports.FEATURE_ASSISTANT_CONTRACTS_V11 = exports.FEATURE_ASSISTANT_INVESTIGATION_STATE_V1 = exports.DEFAULT_PROMPT_VERSION = exports.DEFAULT_MAX_OUTPUT_TOKENS = exports.DEFAULT_TEMPERATURE = exports.DEFAULT_MODEL = exports.DEFAULT_OPENAI_BASE_URL = exports.TIMEZONE = exports.PORT = exports.MODULE_ROOT = exports.BACKEND_ROOT = void 0;
exports.MANUAL_CASE_DECISION_SCHEMA_FILE = exports.ASSISTANT_CAPABILITIES_REGISTRY_FILE = exports.ASSISTANT_CANON_FILE = exports.ARCH_EXPORT_2020_DIR = exports.SCHEMAS_DIR = exports.EVAL_DATASETS_DIR = exports.REPORTS_DIR = exports.PROMPTS_DIR = exports.AUTORUN_GENERATOR_HISTORY_FILE = exports.AUTORUN_GENERATOR_DIR = exports.AUTORUN_ANNOTATIONS_FILE = exports.AUTORUN_ANNOTATIONS_DIR = exports.ASSISTANT_ANNOTATIONS_FILE = exports.ASSISTANT_ANNOTATIONS_DIR = exports.ASSISTANT_SESSIONS_DIR = exports.EVAL_CASES_DIR = exports.PRESETS_DIR = void 0;
exports.MANUAL_CASE_DECISION_SCHEMA_FILE = exports.ASSISTANT_CAPABILITIES_REGISTRY_FILE = exports.ASSISTANT_CANON_FILE = exports.ARCH_EXPORT_2020_DIR = exports.SCHEMAS_DIR = exports.EVAL_DATASETS_DIR = exports.REPORTS_DIR = exports.PROMPTS_DIR = exports.SHARED_LLM_CONNECTION_FILE = exports.AUTORUN_GENERATOR_HISTORY_FILE = exports.AUTORUN_GENERATOR_DIR = exports.AUTORUN_ANNOTATIONS_FILE = exports.AUTORUN_ANNOTATIONS_DIR = exports.ASSISTANT_ANNOTATIONS_FILE = exports.ASSISTANT_ANNOTATIONS_DIR = exports.ASSISTANT_SESSIONS_DIR = exports.EVAL_CASES_DIR = exports.PRESETS_DIR = void 0;
const path_1 = __importDefault(require("path"));
exports.BACKEND_ROOT = path_1.default.resolve(__dirname, "..");
exports.MODULE_ROOT = path_1.default.resolve(exports.BACKEND_ROOT, "..");
@@ -90,6 +90,7 @@ exports.AUTORUN_ANNOTATIONS_DIR = path_1.default.resolve(exports.DATA_DIR, "auto
exports.AUTORUN_ANNOTATIONS_FILE = path_1.default.resolve(exports.AUTORUN_ANNOTATIONS_DIR, "annotations.json");
exports.AUTORUN_GENERATOR_DIR = path_1.default.resolve(exports.DATA_DIR, "autorun_generators");
exports.AUTORUN_GENERATOR_HISTORY_FILE = path_1.default.resolve(exports.AUTORUN_GENERATOR_DIR, "history.json");
exports.SHARED_LLM_CONNECTION_FILE = path_1.default.resolve(exports.DATA_DIR, "shared_llm_connection.json");
exports.PROMPTS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "prompts");
exports.REPORTS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "reports");
exports.EVAL_DATASETS_DIR = path_1.default.resolve(exports.MODULE_ROOT, "eval_cases");
+107
View File
@@ -0,0 +1,107 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildSharedLlmConfigRouter = buildSharedLlmConfigRouter;
const express_1 = require("express");
const config_1 = require("../config");
const http_1 = require("../utils/http");
const files_1 = require("../utils/files");
const path_1 = __importDefault(require("path"));
function sanitizeString(value, fallback) {
if (typeof value !== "string") {
return fallback;
}
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : fallback;
}
function sanitizeNumber(value, fallback) {
if (typeof value === "number" && Number.isFinite(value)) {
return value;
}
if (typeof value === "string" && value.trim().length > 0) {
const parsed = Number(value);
if (Number.isFinite(parsed)) {
return parsed;
}
}
return fallback;
}
function buildFallbackRecord() {
return {
schema_version: "shared_llm_connection_v1",
updated_at: "",
connection: {
llmProvider: "local",
model: "qwen2.5-14b-instruct-1m",
baseUrl: "http://127.0.0.1:1234/v1",
temperature: 0,
maxOutputTokens: 900
}
};
}
function loadSharedRecord() {
const fallback = buildFallbackRecord();
const parsed = (0, files_1.readJsonFile)(config_1.SHARED_LLM_CONNECTION_FILE, null);
if (!parsed || typeof parsed !== "object" || !("connection" in parsed) || !parsed.connection) {
return null;
}
const connection = parsed.connection;
return {
schema_version: "shared_llm_connection_v1",
updated_at: sanitizeString(parsed.updated_at, ""),
connection: {
llmProvider: connection.llmProvider === "local" ? "local" : "openai",
model: sanitizeString(connection.model, fallback.connection.model),
baseUrl: sanitizeString(connection.baseUrl, fallback.connection.baseUrl),
temperature: sanitizeNumber(connection.temperature, fallback.connection.temperature),
maxOutputTokens: Math.max(1, Math.trunc(sanitizeNumber(connection.maxOutputTokens, fallback.connection.maxOutputTokens)))
}
};
}
function buildSharedLlmConfigRouter() {
const router = (0, express_1.Router)();
router.get("/api/llm/shared-connection", (_req, res, next) => {
try {
const record = loadSharedRecord();
(0, http_1.ok)(res, {
ok: true,
connection: record?.connection ?? null,
updated_at: record?.updated_at ?? null,
exists: Boolean(record)
});
}
catch (error) {
next(error);
}
});
router.post("/api/llm/shared-connection", (req, res, next) => {
try {
const body = (req.body ?? {});
const fallback = buildFallbackRecord();
const record = {
schema_version: "shared_llm_connection_v1",
updated_at: new Date().toISOString(),
connection: {
llmProvider: body.llmProvider === "local" ? "local" : "openai",
model: sanitizeString(body.model, fallback.connection.model),
baseUrl: sanitizeString(body.baseUrl, fallback.connection.baseUrl),
temperature: sanitizeNumber(body.temperature, fallback.connection.temperature),
maxOutputTokens: Math.max(1, Math.trunc(sanitizeNumber(body.maxOutputTokens, fallback.connection.maxOutputTokens)))
}
};
(0, files_1.ensureDir)(path_1.default.dirname(config_1.SHARED_LLM_CONNECTION_FILE));
(0, files_1.writeJsonFile)(config_1.SHARED_LLM_CONNECTION_FILE, record);
(0, http_1.ok)(res, {
ok: true,
connection: record.connection,
updated_at: record.updated_at
});
}
catch (error) {
next(error);
}
});
return router;
}
+2
View File
@@ -15,6 +15,7 @@ const eval_1 = require("./routes/eval");
const history_1 = require("./routes/history");
const normalize_1 = require("./routes/normalize");
const presets_1 = require("./routes/presets");
const sharedLlmConfig_1 = require("./routes/sharedLlmConfig");
const testConnection_1 = require("./routes/testConnection");
const inMemoryRuntimeAdapter_1 = require("./runtime/inMemoryRuntimeAdapter");
const assistantService_1 = require("./services/assistantService");
@@ -59,6 +60,7 @@ function createApp() {
});
});
app.use((0, testConnection_1.buildTestConnectionRouter)(openaiClient));
app.use((0, sharedLlmConfig_1.buildSharedLlmConfigRouter)());
app.use((0, normalize_1.buildNormalizeRouter)(services));
app.use((0, eval_1.buildEvalRouter)(services));
app.use((0, assistant_1.buildAssistantRouter)(services));
@@ -1345,7 +1345,7 @@ function hasSelectedObjectInventoryCue(text) {
}
function hasSelectedObjectInventoryProvenanceSignal(text) {
return (hasSelectedObjectInventoryCue(text) &&
/(?:кто\s+(?:(?:это|этот\s+товар|эту\s+позицию)\s+)?(?:нам\s+)?поставил|кто\s+(?:нам\s+)?поставил\s+(?:это|этот\s+товар|эту\s+позицию)|от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|у\s+кого\s+купили|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+куплено|supplier|vendor|поставщик)/iu.test(text));
/(?:кто\s+(?:(?:это|этот\s+товар|эту\s+позицию)\s+)?(?:нам\s+)?поставил|кто\s+(?:нам\s+)?поставил\s+(?:это|этот\s+товар|эту\s+позицию)|от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|у\s+кого\s+купили|у\s+кого\s+куплено|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+куплено|supplier|vendor|поставщик)/iu.test(text));
}
function hasSelectedObjectInventoryPurchaseDocumentsSignal(text) {
return (hasSelectedObjectInventoryCue(text) &&
@@ -1353,7 +1353,7 @@ function hasSelectedObjectInventoryPurchaseDocumentsSignal(text) {
}
function hasInventoryProvenanceSignalV2(text) {
const hasItemCue = /(?:товар|номенклатур|sku|item|product|остат(?:ок|ки)|склад)/iu.test(text);
const hasSupplierCue = /(?:от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|у\s+кого\s+купили|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|товар|позицию))?|где\s+куплено|кто\s+(?:нам\s+)?поставил|кем\s+поставлен|поставщик|supplier|vendor)/iu.test(text);
const hasSupplierCue = /(?:от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|у\s+кого\s+купили|у\s+кого\s+куплено|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|товар|позицию))?|где\s+куплено|кто\s+(?:нам\s+)?поставил|кем\s+поставлен|поставщик|supplier|vendor)/iu.test(text);
const hasPurchaseCue = /(?:куплен(?:ы|а|о)?|закупк|происхождени|откуда|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|товар|позицию))?|где\s+куплено|когда\s+был\s+куплен|когда\s+куплен|дата\s+закупк|кто\s+(?:нам\s+)?поставил|кем\s+поставлен|поставлен(?:ы|а)?|purchase\s+provenance|purchase\s+date)/iu.test(text);
return hasItemCue && hasSupplierCue && hasPurchaseCue;
}
@@ -269,7 +269,7 @@ function hasSelectedObjectInventoryFollowupSignal(text) {
if (!/(?:по\s+выбранному\s+объекту|по\s+выбранной\s+позиции)/iu.test(text)) {
return false;
}
return /(?:у\s+кого\s+купили|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|товар|позицию))?|где\s+куплено|кто\s+(?:поставил|продал)|кому\s+(?:продали|реализовали)|когда\s+(?:примерно\s+)?купили|по\s+каким\s+документам\s+.*купили)/iu.test(text);
return /(?:у\s+кого\s+купили|у\s+кого\s+куплено|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|товар|позицию))?|где\s+куплено|кто\s+(?:поставил|продал)|кому\s+(?:продали|реализовали)|когда\s+(?:примерно\s+)?купили|по\s+каким\s+документам\s+.*купили)/iu.test(text);
}
function hasDocsOrBankSignal(text) {
return /(?:док(?:и|умент|ументы|ументов)|docs?|documents?|банк|выписк|платеж|платёж|оплат|поступлен|списан|транзак|transactions?|bank\s+ops|bank\s+operations?)/iu.test(text);
@@ -25,7 +25,7 @@ function hasSameDateHint(text) {
return /(?:на\s+ту\s+же\s+дат[ауеы]|на\s+эту\s+же\s+дат[ауеы]|на\s+эту\s+дат[ауеы]|эту\s+дат[ауеы]|та\s+же\s+дата|same\s+date|as\s+of\s+same\s+date|the\s+same\s+date)/iu.test(String(text ?? ""));
}
function hasExplicitPeriodLiteral(text) {
return /\b(?:19|20)\d{2}(?:[./-](?:0?[1-9]|1[0-2]))?\b/.test(String(text ?? ""));
return /(?:^|[^\d*×xх])((?:19|20)\d{2}(?:[./-](?:0?[1-9]|1[0-2]))?)(?=$|[^\d*×xх])/iu.test(String(text ?? ""));
}
function hasExplicitCurrentDateHint(text) {
return /(?:на\s+текущ(?:ую|ая|ий|ее|ей|ем|его)\s+дат(?:у|а|е|ой|ою)|на\s+сегодняшн(?:юю|ий|ей|ем|его)\s+дат(?:у|а|е|ой|ою)|на\s+сегодня|сегодня|на\s+текущ(?:ий|ую)\s+момент|today|as\s+of\s+today|current\s+date|as\s+of\s+current\s+date)/iu.test(String(text ?? ""));
@@ -257,7 +257,7 @@ function hasSelectedObjectInventorySignal(text) {
return /(?:по\s+выбранному\s+объекту|for\s+selected\s+object)/iu.test(String(text ?? ""));
}
function hasInventorySupplierFollowupCue(text) {
return /(?:кто\s+(?:(?:это|этот\s+товар|эту\s+позицию)\s+)?(?:нам\s+)?поставил|кто\s+(?:нам\s+)?поставил\s+(?:это|этот\s+товар|эту\s+позицию)|от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|у\s+кого\s+купили|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+куплено|supplier|vendor|поставщик)/iu.test(String(text ?? ""));
return /(?:кто\s+(?:(?:это|этот\s+товар|эту\s+позицию)\s+)?(?:нам\s+)?поставил|кто\s+(?:нам\s+)?поставил\s+(?:это|этот\s+товар|эту\s+позицию)|от\s+какого\s+поставщика|у\s+какого\s+поставщика|от\s+кого\s+куплен|у\s+кого\s+купили|у\s+кого\s+куплено|где\s+(?:мы\s+)?купили(?:\s+(?:это|его|этот\s+товар|эту\s+позицию))?|где\s+куплено|supplier|vendor|поставщик)/iu.test(String(text ?? ""));
}
function hasInventoryPurchaseDocumentsFollowupCue(text) {
return /(?:по\s+каким\s+документам\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|по\s+каким\s+документам\s+(?:был\s+)?куплен|какими\s+документами\s+(?:это|его|этот\s+товар|эту\s+позицию)\s+купили|какими\s+документами\s+(?:был\s+)?куплен|покажи\s+документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|документы\s+по\s+(?:этой\s+позиции|этому\s+товару|ней|нему)|purchase\s+documents|documents\s+of\s+purchase|through\s+which\s+documents)/iu.test(String(text ?? ""));