@iola_adm/iola-cli 0.1.69 → 0.1.71
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 +37 -14
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -766,7 +766,7 @@ async function startAgentReadline() {
|
|
|
766
766
|
}
|
|
767
767
|
|
|
768
768
|
async function startAgentRawInput() {
|
|
769
|
-
const state = { history: [], buffer: "", selected: 0, slashOffset: 0, slashOpen: false, running: false, renderedInputLines: 0, rawMode: true, pendingOutput: "", aiStatus: null };
|
|
769
|
+
const state = { history: [], buffer: "", selected: 0, slashOffset: 0, slashOpen: false, running: false, renderedInputLines: 0, renderedLines: 0, rawMode: true, pendingOutput: "", aiStatus: null };
|
|
770
770
|
const wasRaw = input.isRaw;
|
|
771
771
|
activateRawInput(input);
|
|
772
772
|
|
|
@@ -1330,40 +1330,52 @@ function renderAgentInput(state) {
|
|
|
1330
1330
|
output.write(`\x1b[${cursorColumn + 1}G`);
|
|
1331
1331
|
}
|
|
1332
1332
|
state.renderedInputLines = inputLines.length;
|
|
1333
|
+
state.renderedLines = renderedLines.length;
|
|
1333
1334
|
}
|
|
1334
1335
|
|
|
1335
1336
|
function clearAgentInputArea(state = null) {
|
|
1336
1337
|
if (!output.isTTY) return;
|
|
1337
|
-
const
|
|
1338
|
-
if (
|
|
1338
|
+
const renderedLines = Math.max(1, Number(state?.renderedLines || state?.renderedInputLines || 1));
|
|
1339
|
+
if (renderedLines > 1) output.write(`\x1b[${renderedLines - 1}A`);
|
|
1339
1340
|
output.write("\r\x1b[0J");
|
|
1340
|
-
if (state)
|
|
1341
|
+
if (state) {
|
|
1342
|
+
state.renderedInputLines = 0;
|
|
1343
|
+
state.renderedLines = 0;
|
|
1344
|
+
}
|
|
1341
1345
|
}
|
|
1342
1346
|
|
|
1343
1347
|
function startActivityIndicator(label = "работаю") {
|
|
1344
1348
|
const doneLabel = "готово";
|
|
1345
1349
|
if (!output.isTTY || process.env.NO_COLOR === "1") {
|
|
1346
|
-
output.write(`${label}
|
|
1350
|
+
output.write(`${formatActivityLine(label)}\n`);
|
|
1347
1351
|
const started = Date.now();
|
|
1348
1352
|
return () => {
|
|
1349
1353
|
const seconds = ((Date.now() - started) / 1000).toFixed(1);
|
|
1350
|
-
output.write(
|
|
1354
|
+
output.write(`${formatActivityLine(doneLabel, seconds)}\n`);
|
|
1351
1355
|
};
|
|
1352
1356
|
}
|
|
1353
1357
|
const started = Date.now();
|
|
1354
1358
|
const render = () => {
|
|
1355
1359
|
const seconds = ((Date.now() - started) / 1000).toFixed(1);
|
|
1356
|
-
output.write(`\r\x1b[2K${colorMuted(
|
|
1360
|
+
output.write(`\r\x1b[2K${colorMuted(formatActivityLine(label, seconds))}`);
|
|
1357
1361
|
};
|
|
1358
1362
|
render();
|
|
1359
1363
|
const timer = setInterval(render, 120);
|
|
1360
1364
|
return () => {
|
|
1361
1365
|
clearInterval(timer);
|
|
1362
1366
|
const seconds = ((Date.now() - started) / 1000).toFixed(1);
|
|
1363
|
-
output.write(`\r\x1b[2K${colorMuted(
|
|
1367
|
+
output.write(`\r\x1b[2K${colorMuted(formatActivityLine(doneLabel, seconds))}\n`);
|
|
1364
1368
|
};
|
|
1365
1369
|
}
|
|
1366
1370
|
|
|
1371
|
+
function formatActivityLine(label, seconds = null) {
|
|
1372
|
+
const columns = Math.max(60, Number(output.columns || 100));
|
|
1373
|
+
const middle = ` ${label}${seconds == null ? "" : ` ${seconds}s`} `;
|
|
1374
|
+
const leftWidth = Math.max(1, Math.floor((columns - visibleLength(middle)) / 3));
|
|
1375
|
+
const rightWidth = Math.max(1, columns - leftWidth - visibleLength(middle));
|
|
1376
|
+
return `${"─".repeat(leftWidth)}${middle}${"─".repeat(rightWidth)}`;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1367
1379
|
function suspendRawInputForCommand(stream) {
|
|
1368
1380
|
if (!stream.isTTY || !stream.isRaw) return () => {};
|
|
1369
1381
|
stream.setRawMode(false);
|
|
@@ -4085,12 +4097,15 @@ async function listAiModels(provider) {
|
|
|
4085
4097
|
}
|
|
4086
4098
|
|
|
4087
4099
|
const payload = await response.json();
|
|
4088
|
-
const
|
|
4100
|
+
const installed = (payload.models || []).map((model) => ({
|
|
4089
4101
|
id: model.name,
|
|
4090
4102
|
provider: "ollama",
|
|
4091
4103
|
note: model.modified_at ? `updated ${model.modified_at}` : "local",
|
|
4092
4104
|
}));
|
|
4093
|
-
|
|
4105
|
+
const installedIds = new Set(installed.map((model) => model.id));
|
|
4106
|
+
const recommended = getRecommendedOllamaModels("not installed")
|
|
4107
|
+
.filter((model) => !installedIds.has(model.id));
|
|
4108
|
+
return [...installed, ...recommended];
|
|
4094
4109
|
} catch {
|
|
4095
4110
|
return getRecommendedOllamaModels("recommended");
|
|
4096
4111
|
}
|
|
@@ -4145,10 +4160,12 @@ async function listAiModels(provider) {
|
|
|
4145
4160
|
|
|
4146
4161
|
function getRecommendedOllamaModels(notePrefix = "recommended") {
|
|
4147
4162
|
return [
|
|
4148
|
-
{ id: "
|
|
4149
|
-
{ id: "
|
|
4150
|
-
{ id: "
|
|
4151
|
-
{ id: "
|
|
4163
|
+
{ id: "qwen3:1.7b", provider: "ollama", note: `${notePrefix} recommended low RAM` },
|
|
4164
|
+
{ id: "qwen3:4b", provider: "ollama", note: `${notePrefix} recommended balanced` },
|
|
4165
|
+
{ id: "gemma3:1b", provider: "ollama", note: `${notePrefix} Gemma low RAM` },
|
|
4166
|
+
{ id: "gemma3:4b", provider: "ollama", note: `${notePrefix} Gemma balanced` },
|
|
4167
|
+
{ id: "llama3.2:3b", provider: "ollama", note: `${notePrefix} legacy fallback` },
|
|
4168
|
+
{ id: "llama3.2:1b", provider: "ollama", note: `${notePrefix} minimal fallback only` },
|
|
4152
4169
|
];
|
|
4153
4170
|
}
|
|
4154
4171
|
|
|
@@ -6567,7 +6584,10 @@ async function buildAiMessages(question, dataContext, history, options = {}, con
|
|
|
6567
6584
|
const system = [
|
|
6568
6585
|
"Ты терминальный AI-агент городского округа Йошкар-Ола.",
|
|
6569
6586
|
"Отвечай на русском языке естественно и по смыслу запроса пользователя.",
|
|
6587
|
+
"Не смешивай языки. Не выдумывай факты, географию и числа.",
|
|
6588
|
+
"Если пользователь просто здоровается, ответь коротким приветствием и спроси, чем помочь.",
|
|
6570
6589
|
hasDataContext ? "Используй только данные из переданного контекста открытых данных." : "Для обычного диалога отвечай как полноценный AI-ассистент, не перечисляй слои и возможности без запроса пользователя.",
|
|
6590
|
+
hasDataContext ? "" : "Не рассказывай сведения о Йошкар-Оле, школах или детских садах без прямого запроса и контекста данных.",
|
|
6571
6591
|
hasDataContext ? "Если в контексте нет нужных сведений, прямо напиши, что данных недостаточно." : "",
|
|
6572
6592
|
hasDataContext ? "Не выдумывай адреса, телефоны, лицензии и руководителей." : "",
|
|
6573
6593
|
hasDataContext ? "Если отвечаешь по конкретным организациям, укажи источник в конце: слой, название и ИНН." : "",
|
|
@@ -6673,6 +6693,9 @@ async function callOllama(config, messages) {
|
|
|
6673
6693
|
model: config.model || "llama3.2:1b",
|
|
6674
6694
|
messages,
|
|
6675
6695
|
stream: false,
|
|
6696
|
+
options: {
|
|
6697
|
+
temperature: Number(config.temperature ?? 0.1),
|
|
6698
|
+
},
|
|
6676
6699
|
}),
|
|
6677
6700
|
});
|
|
6678
6701
|
} catch {
|