@iola_adm/iola-cli 0.1.112 → 0.1.113
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 +53 -7
- package/test/smoke-test.js +2 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -4372,8 +4372,8 @@ async function listAiModels(provider) {
|
|
|
4372
4372
|
if (getAiNetworkMode(relayConfig) === "gateway") {
|
|
4373
4373
|
const payload = await callAiRelayModels(relayConfig, apiKey, "OpenAI");
|
|
4374
4374
|
return (payload.data || [])
|
|
4375
|
-
.map(
|
|
4376
|
-
.sort(
|
|
4375
|
+
.map(mapOpenAiModel)
|
|
4376
|
+
.sort(sortModelsByFreshness);
|
|
4377
4377
|
}
|
|
4378
4378
|
const response = await fetch("https://api.openai.com/v1/models", {
|
|
4379
4379
|
headers: { authorization: `Bearer ${apiKey}` },
|
|
@@ -4385,8 +4385,8 @@ async function listAiModels(provider) {
|
|
|
4385
4385
|
|
|
4386
4386
|
const payload = await response.json();
|
|
4387
4387
|
return (payload.data || [])
|
|
4388
|
-
.map(
|
|
4389
|
-
.sort(
|
|
4388
|
+
.map(mapOpenAiModel)
|
|
4389
|
+
.sort(sortModelsByFreshness);
|
|
4390
4390
|
}
|
|
4391
4391
|
|
|
4392
4392
|
if (provider === "openrouter") {
|
|
@@ -4431,6 +4431,44 @@ async function getApiProviderNetworkConfig(provider) {
|
|
|
4431
4431
|
};
|
|
4432
4432
|
}
|
|
4433
4433
|
|
|
4434
|
+
function mapOpenAiModel(model) {
|
|
4435
|
+
const id = String(model.id || "");
|
|
4436
|
+
const created = Number(model.created || inferOpenAiModelCreated(id) || 0);
|
|
4437
|
+
return {
|
|
4438
|
+
id,
|
|
4439
|
+
provider: "openai",
|
|
4440
|
+
note: model.owned_by || "",
|
|
4441
|
+
created,
|
|
4442
|
+
releaseDate: formatUnixDate(created),
|
|
4443
|
+
};
|
|
4444
|
+
}
|
|
4445
|
+
|
|
4446
|
+
function isOpenAiTextGenerationModel(model) {
|
|
4447
|
+
const id = String(model.id || "").toLocaleLowerCase("en-US");
|
|
4448
|
+
if (!id) return false;
|
|
4449
|
+
if (/^(babbage|davinci|text-|whisper|tts|dall-|omni-|computer-|codex-mini-latest)/.test(id)) return false;
|
|
4450
|
+
if (/(image|audio|tts|transcribe|realtime|embedding|moderation|search|instruct|safeguard)/.test(id)) return false;
|
|
4451
|
+
if (/^gpt-3\.5/.test(id)) return false;
|
|
4452
|
+
return id === "chat-latest"
|
|
4453
|
+
|| /^gpt-(4|4o|5|5\.)/.test(id)
|
|
4454
|
+
|| /^o[134](?:-|$)/.test(id);
|
|
4455
|
+
}
|
|
4456
|
+
|
|
4457
|
+
function dedupeDatedOpenAiModels(models) {
|
|
4458
|
+
const ids = new Set(models.map((model) => model.id));
|
|
4459
|
+
return models.filter((model) => {
|
|
4460
|
+
const base = model.id.replace(/-\d{4}-\d{2}-\d{2}$/, "");
|
|
4461
|
+
return base === model.id || !ids.has(base);
|
|
4462
|
+
});
|
|
4463
|
+
}
|
|
4464
|
+
|
|
4465
|
+
function inferOpenAiModelCreated(id) {
|
|
4466
|
+
const match = String(id || "").match(/(\d{4})-(\d{2})-(\d{2})$/);
|
|
4467
|
+
if (!match) return 0;
|
|
4468
|
+
const [, year, month, day] = match;
|
|
4469
|
+
return Math.floor(Date.UTC(Number(year), Number(month) - 1, Number(day)) / 1000);
|
|
4470
|
+
}
|
|
4471
|
+
|
|
4434
4472
|
function mapOpenRouterModel(model) {
|
|
4435
4473
|
const id = String(model.id || "");
|
|
4436
4474
|
const architecture = model.architecture || {};
|
|
@@ -4834,19 +4872,27 @@ async function chooseAiModel(provider) {
|
|
|
4834
4872
|
? models.filter((model) => model.id.toLocaleLowerCase("ru-RU").includes(search.toLocaleLowerCase("ru-RU")))
|
|
4835
4873
|
: models;
|
|
4836
4874
|
|
|
4875
|
+
if (provider === "openai") {
|
|
4876
|
+
filtered = dedupeDatedOpenAiModels(filtered.filter(isOpenAiTextGenerationModel))
|
|
4877
|
+
.sort(sortModelsByFreshness);
|
|
4878
|
+
}
|
|
4879
|
+
|
|
4837
4880
|
if (filtered.length === 0) {
|
|
4838
4881
|
console.log("Модели не найдены.");
|
|
4839
4882
|
return "";
|
|
4840
4883
|
}
|
|
4841
4884
|
|
|
4842
|
-
const limit = 25;
|
|
4885
|
+
const limit = provider === "openai" ? 30 : 25;
|
|
4843
4886
|
if (filtered.length > limit) {
|
|
4844
4887
|
filtered = filtered.slice(0, limit);
|
|
4845
|
-
console.log(`Показаны первые ${limit}
|
|
4888
|
+
console.log(`Показаны первые ${limit} моделей.`);
|
|
4846
4889
|
}
|
|
4847
4890
|
|
|
4848
4891
|
console.log("Выберите модель:");
|
|
4849
|
-
filtered.forEach((model, index) =>
|
|
4892
|
+
filtered.forEach((model, index) => {
|
|
4893
|
+
const date = model.releaseDate ? ` (${model.releaseDate})` : "";
|
|
4894
|
+
console.log(` ${index + 1}. ${model.id}${date}${model.note ? ` - ${model.note}` : ""}`);
|
|
4895
|
+
});
|
|
4850
4896
|
console.log(" 0. Отмена");
|
|
4851
4897
|
|
|
4852
4898
|
const answer = Number(await askText("Номер: "));
|
package/test/smoke-test.js
CHANGED
|
@@ -53,6 +53,8 @@ assertIncludes(cliSource, "console.log(\" 0. Назад\")", "OpenRouter model
|
|
|
53
53
|
assertIncludes(cliSource, "renderTerminalMarkdown", "AI answers should render inline markdown in the terminal");
|
|
54
54
|
assertIncludes(cliSource, "\\x1b[1m$1\\x1b[22m", "AI answer renderer should support bold markdown");
|
|
55
55
|
assertIncludes(cliSource, "ensureApiKeyForModelSelection", "API model selection should prompt for missing provider keys");
|
|
56
|
+
assertIncludes(cliSource, "isOpenAiTextGenerationModel", "OpenAI model selection should filter technical and legacy models");
|
|
57
|
+
assertIncludes(cliSource, "dedupeDatedOpenAiModels", "OpenAI model selection should hide dated duplicates when aliases exist");
|
|
56
58
|
|
|
57
59
|
const commands = await runCli(["commands"]);
|
|
58
60
|
assertIncludes(commands, "iola browser status|install|open|text|html|screenshot|pdf|click|type|eval", "commands");
|