@iola_adm/iola-cli 0.1.71 → 0.1.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/cli.js +66 -4
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -5969,6 +5969,20 @@ async function aiAsk(args, context = {}) {
|
|
|
5969
5969
|
const historyEnabled = !options.bare && !options["no-history"] && isFeatureEnabled("sqlite-history");
|
|
5970
5970
|
const sessionId = historyEnabled && isFeatureEnabled("sessions") ? ensureSessionForAsk(options, providerConfig, question) : null;
|
|
5971
5971
|
const history = context.history || (sessionId ? getSessionAiHistory(sessionId) : []);
|
|
5972
|
+
const directAnswer = buildDirectDataAnswer(question, dataContext);
|
|
5973
|
+
if (directAnswer) {
|
|
5974
|
+
if (historyEnabled) {
|
|
5975
|
+
recordAskHistory({ question, answer: directAnswer, providerConfig, dataContext, error: "", sessionId });
|
|
5976
|
+
appendSessionExchange(sessionId, question, directAnswer, dataContext, "");
|
|
5977
|
+
}
|
|
5978
|
+
emitEvent(options, "answer", { length: directAnswer.length, sessionId, direct: true });
|
|
5979
|
+
if (options.output) {
|
|
5980
|
+
await assertPermission("writeFiles");
|
|
5981
|
+
await writeFile(options.output, directAnswer, "utf8");
|
|
5982
|
+
}
|
|
5983
|
+
if (!options.quiet) console.log(directAnswer);
|
|
5984
|
+
return directAnswer;
|
|
5985
|
+
}
|
|
5972
5986
|
const messages = await buildAiMessages(question, dataContext, history, options, config);
|
|
5973
5987
|
let answer = "";
|
|
5974
5988
|
let errorMessage = "";
|
|
@@ -6018,6 +6032,28 @@ async function aiAsk(args, context = {}) {
|
|
|
6018
6032
|
return answer;
|
|
6019
6033
|
}
|
|
6020
6034
|
|
|
6035
|
+
function buildDirectDataAnswer(question, dataContext) {
|
|
6036
|
+
const normalized = question.toLocaleLowerCase("ru-RU");
|
|
6037
|
+
if (!/(директор|руководител)/iu.test(normalized)) return "";
|
|
6038
|
+
const terms = extractSearchTerms(question).filter((term) => !/^\d+$/.test(term));
|
|
6039
|
+
if (terms.length < 2) return "";
|
|
6040
|
+
const rows = [
|
|
6041
|
+
...dataContext.schools.map((item) => ({ layer: "schools", layerName: "школы", ...item })),
|
|
6042
|
+
...dataContext.kindergartens.map((item) => ({ layer: "kindergartens", layerName: "детские сады", ...item })),
|
|
6043
|
+
];
|
|
6044
|
+
const matches = rows.filter((item) => {
|
|
6045
|
+
const head = String(item.head || "").toLocaleLowerCase("ru-RU");
|
|
6046
|
+
return terms.every((term) => head.includes(term));
|
|
6047
|
+
});
|
|
6048
|
+
if (matches.length !== 1) return "";
|
|
6049
|
+
const item = matches[0];
|
|
6050
|
+
return [
|
|
6051
|
+
`${item.head} — руководитель ${item.name}.`,
|
|
6052
|
+
item.address ? `Адрес: ${item.address}.` : "",
|
|
6053
|
+
`Источник: слой ${item.layer}, ИНН ${item.inn || "-"}.`,
|
|
6054
|
+
].filter(Boolean).join("\n");
|
|
6055
|
+
}
|
|
6056
|
+
|
|
6021
6057
|
async function resolveUsableAiProfile(config, options = {}) {
|
|
6022
6058
|
const explicit = Boolean(options.profile || options.provider);
|
|
6023
6059
|
const providerConfig = resolveAiProfile(config, options);
|
|
@@ -6435,8 +6471,8 @@ async function buildDataContext(question) {
|
|
|
6435
6471
|
const mcpBaseUrl = await getMcpBaseUrl();
|
|
6436
6472
|
const [layers, schools, kindergartens] = await Promise.all([
|
|
6437
6473
|
fetchJson(`${mcpBaseUrl}/mcp-version`),
|
|
6438
|
-
|
|
6439
|
-
|
|
6474
|
+
fetchAllApiItems(`${apiBaseUrl}/schools`),
|
|
6475
|
+
fetchAllApiItems(`${apiBaseUrl}/kindergartens`),
|
|
6440
6476
|
]);
|
|
6441
6477
|
const queryTerms = extractSearchTerms(question);
|
|
6442
6478
|
const patterns = extractStructuredPatterns(question);
|
|
@@ -6461,6 +6497,18 @@ async function buildDataContext(question) {
|
|
|
6461
6497
|
};
|
|
6462
6498
|
}
|
|
6463
6499
|
|
|
6500
|
+
async function fetchAllApiItems(endpoint, limit = 500, maxItems = 5000) {
|
|
6501
|
+
const all = [];
|
|
6502
|
+
for (let offset = 0; offset < maxItems; offset += limit) {
|
|
6503
|
+
const separator = endpoint.includes("?") ? "&" : "?";
|
|
6504
|
+
const payload = await fetchJson(`${endpoint}${separator}limit=${limit}&offset=${offset}`);
|
|
6505
|
+
const items = normalizeItems(payload);
|
|
6506
|
+
all.push(...items);
|
|
6507
|
+
if (items.length < limit) break;
|
|
6508
|
+
}
|
|
6509
|
+
return all;
|
|
6510
|
+
}
|
|
6511
|
+
|
|
6464
6512
|
function emptyDataContext(question) {
|
|
6465
6513
|
return {
|
|
6466
6514
|
enabled: false,
|
|
@@ -6481,7 +6529,13 @@ function shouldUseDataContext(question, options = {}) {
|
|
|
6481
6529
|
if (/^(привет|здравствуй|здравствуйте|добрый день|доброе утро|добрый вечер|hi|hello|hey)[!.?\s]*$/iu.test(normalized)) return false;
|
|
6482
6530
|
if (/^(спасибо|благодарю|ок|окей|понял|поняла|ясно|хорошо|да|нет)[!.?\s]*$/iu.test(normalized)) return false;
|
|
6483
6531
|
if (normalized.length <= 24 && /^(как дела|что нового|ты тут|ты здесь|кто ты)[?.!\s]*$/iu.test(normalized)) return false;
|
|
6484
|
-
|
|
6532
|
+
const dataKeywords = [
|
|
6533
|
+
"школ", "сад", "детсад", "детский сад", "лицей", "гимнази", "инн", "адрес", "телефон",
|
|
6534
|
+
"почт", "email", "сайт", "лиценз", "руководител", "директор", "слой", "слои", "данн",
|
|
6535
|
+
"отчет", "отчёт", "выгруз", "csv", "json", "найди", "покажи", "список", "карточк",
|
|
6536
|
+
"организац", "учрежден", "йошкар", "ола", "петрова", "строител", "советск", "первомайск",
|
|
6537
|
+
];
|
|
6538
|
+
return dataKeywords.some((keyword) => normalized.includes(keyword));
|
|
6485
6539
|
}
|
|
6486
6540
|
|
|
6487
6541
|
function extractSearchTerms(question) {
|
|
@@ -6491,7 +6545,13 @@ function extractSearchTerms(question) {
|
|
|
6491
6545
|
.split(/\s+/)
|
|
6492
6546
|
.map((term) => term.trim())
|
|
6493
6547
|
.filter(Boolean)
|
|
6494
|
-
.filter((term) => ![
|
|
6548
|
+
.filter((term) => ![
|
|
6549
|
+
"в", "во", "на", "по", "и", "а", "ну", "так", "слушай", "скажи", "подскажи",
|
|
6550
|
+
"какие", "какая", "какой", "каком", "какой", "есть", "найди", "покажи",
|
|
6551
|
+
"контакты", "адрес", "телефон", "школы", "школа", "школе", "сад", "детский",
|
|
6552
|
+
"детские", "сады", "улица", "ул", "директор", "руководитель",
|
|
6553
|
+
].includes(term))
|
|
6554
|
+
.filter((term) => term.length > 2 || /^\d+$/.test(term));
|
|
6495
6555
|
|
|
6496
6556
|
return normalized.length > 0 ? normalized : [question];
|
|
6497
6557
|
}
|
|
@@ -6539,8 +6599,10 @@ function scoreItem(item, terms, patterns, layer) {
|
|
|
6539
6599
|
const text = JSON.stringify(summary).toLocaleLowerCase("ru-RU");
|
|
6540
6600
|
const name = String(summary.name || "").toLocaleLowerCase("ru-RU");
|
|
6541
6601
|
const address = String(summary.address || "").toLocaleLowerCase("ru-RU");
|
|
6602
|
+
const head = String(summary.head || "").toLocaleLowerCase("ru-RU");
|
|
6542
6603
|
const generalTerms = terms.filter((term) => !/^\d+$/.test(term));
|
|
6543
6604
|
let score = generalTerms.reduce((value, term) => value + (text.includes(term.toLocaleLowerCase("ru-RU")) ? 1 : 0), 0);
|
|
6605
|
+
score += generalTerms.reduce((value, term) => value + (head.includes(term.toLocaleLowerCase("ru-RU")) ? 5 : 0), 0);
|
|
6544
6606
|
|
|
6545
6607
|
for (const inn of patterns.inns) {
|
|
6546
6608
|
if (String(summary.inn) === inn) {
|