odant-work 1.0.14 → 1.0.16
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/#system/package-manifest.json +54 -46
- package/$server/$folder/handlers/methods/create/$handler/class.js +83 -2
- package/$server/$folder/lib/icon/icon.js +14 -0
- package/$server/$folder/lib/node/node.js +51 -1
- package/$server/$folder/lib/tree/tree.js +5 -1
- package/.secretsignore +4 -0
- package/MODELS/$ai/$folder/$class/$ai/class.js +57 -2
- package/MODELS/$ai/$folder/$class/$provider/class.js +1 -1
- package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/provider/$handler/class.js +13 -0
- package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/provider/$handler/provider.js +479 -0
- package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/trainer/$handler/trainer.js +3 -3
- package/MODELS/GigaChat/$provider/$folder/$class/$ai/class.js +10 -20
- package/MODELS/GigaChat/$provider/class.js +10 -7
- package/MODELS/GigaChat/GigaChat 2 Lite/$ai/class.js +15 -0
- package/MODELS/GigaChat/GigaChat 2 Max/$ai/class.js +16 -0
- package/MODELS/GigaChat/GigaChat 2 Pro/$ai/class.js +15 -0
- package/MODELS/GigaChat/GigaChat Embeddings/$ai/class.js +13 -0
- package/MODELS/Local/$provider/$folder/$class/$ai/class.js +3 -3
- package/MODELS/Local/$provider/class.js +7 -3
- package/MODELS/MTS-AI/$provider/$folder/$class/$ai/class.js +8 -0
- package/MODELS/MTS-AI/$provider/class.js +8 -0
- package/MODELS/MTS-AI/Cotype Lite/$ai/class.js +15 -0
- package/MODELS/MTS-AI/Cotype Pro 2/$ai/class.js +15 -0
- package/MODELS/MTS-AI/Cotype Vision/$ai/class.js +14 -0
- package/MODELS/T-Bank/$provider/$folder/$class/$ai/class.js +8 -0
- package/MODELS/T-Bank/$provider/class.js +8 -0
- package/MODELS/T-Bank/T-Lite 2.0/$ai/class.js +15 -0
- package/MODELS/T-Bank/T-Pro 2.0/$ai/class.js +16 -0
- package/MODELS/T-Bank/T-VoiceKit ASR/$ai/class.js +13 -0
- package/MODELS/T-Bank/T-VoiceKit TTS/$ai/class.js +13 -0
- package/MODELS/Yandex/$provider/$folder/$class/$ai/class.js +9 -0
- package/MODELS/Yandex/$provider/class.js +9 -0
- package/MODELS/Yandex/Alice AI (YandexGPT 5 Pro)/$ai/class.js +15 -0
- package/MODELS/Yandex/Yandex Embeddings/$ai/class.js +13 -0
- package/MODELS/Yandex/Yandex SpeechKit ASR/$ai/class.js +13 -0
- package/MODELS/Yandex/Yandex SpeechKit TTS/$ai/class.js +13 -0
- package/MODELS/Yandex/YandexART/$ai/class.js +13 -0
- package/MODELS/Yandex/YandexGPT 5 Lite/$ai/class.js +15 -0
- package/MODELS/Yandex/YandexGPT Reasoning/$ai/class.js +22 -0
- package/oda/tools/icons/lib/svg/ai.svg +20 -0
- package/package.json +1 -1
- package/scripts/check-ignored-code.mjs +3 -0
- package/scripts/check-secrets.mjs +1 -1
- package/sources/data/progress.md +17 -0
- package/sources/host/auth-methods.js +32 -5
- package/sources/host/mail.js +93 -14
- package/sources/host/watch.js +13 -2
- package/sources/modules/agent/index.js +23 -2
- package/sources/modules/trainer/halogen.js +128 -29
- package/sources/modules/trainer/huggingface.js +11 -5
- package/sources/modules/trainer/index.js +135 -2
- package/sources/modules/trainer/providers.js +1213 -0
- package/sources/modules/user-profile/user-profile.js +7 -1
- package/sources/server/class.js +55 -3
- package/MODELS/GigaChat/GigaChat Light/$ai/class.js +0 -15
- package/MODELS/GigaChat/GigaChat Pro/$ai/class.js +0 -15
- package/MODELS/Halogen/$provider/$folder/$class/$ai/class.js +0 -11
- package/MODELS/Halogen/$provider/class.js +0 -16
- package/MODELS/Halogen/$provider/readme.md +0 -28
- package/MODELS/Halogen/Decider-NPU/$ai/class.js +0 -22
- package/MODELS/Halogen/Flux2-Klein-NPU/$ai/class.js +0 -21
- package/MODELS/Halogen/Qwen3-ASR-NPU/$ai/class.js +0 -21
- package/MODELS/Halogen/Qwen3-Embedding-NPU/$ai/class.js +0 -22
- package/MODELS/Halogen/Qwen3-Reranker-NPU/$ai/class.js +0 -21
- package/MODELS/Halogen/Qwen3-TTS-NPU/$ai/class.js +0 -26
- package/MODELS/Halogen/Qwen3.5-2B-NPU/$ai/class.js +0 -23
- package/MODELS/Halogen/Qwen3.8-Flash-Next/$ai/class.js +0 -31
- package/MODELS/Local/Flux-Image/$ai/class.js +0 -20
- package/MODELS/Local/Qwen3-0.6B/$ai/class.js +0 -26
- package/MODELS/Local/Qwen3-14B/$ai/class.js +0 -27
- package/MODELS/Local/Qwen3-30B-A3B/$ai/class.js +0 -30
- package/MODELS/Local/Qwen3-Embedding/$ai/class.js +0 -21
- package/MODELS/Local/Qwen3-Voice/$ai/class.js +0 -26
- package/MODELS/odant/$provider/$folder/$class/$ai/class.js +0 -11
- package/MODELS/odant/$provider/class.js +0 -75
- package/MODELS/odant/$provider/readme.md +0 -21
- package/MODELS/odant/Qwen3-ASR/$ai/class.js +0 -15
- package/MODELS/odant/Qwen3-TTS/$ai/class.js +0 -22
- package/MODELS/odant/Qwen3.8 27b/$ai/class.js +0 -11
- package/MODELS/odant/Z-Image-Turbo/$ai/class.js +0 -8
- package/MODELS/odant/Z-Image-Turbo/$ai/readme.md +0 -24
|
@@ -34,7 +34,7 @@ import http from 'node:http';
|
|
|
34
34
|
import https from 'node:https';
|
|
35
35
|
import crypto from 'node:crypto';
|
|
36
36
|
import { spawn } from 'node:child_process';
|
|
37
|
-
import { startHuggingFaceDownload, listHuggingFaceDownloads } from './huggingface.js';
|
|
37
|
+
import { startHuggingFaceDownload, listHuggingFaceDownloads, registerLocalModelNode } from './huggingface.js';
|
|
38
38
|
|
|
39
39
|
export const HALOGEN_DEFAULT_PORT = Number(process.env.HALOGEN_API_PORT) || 8731;
|
|
40
40
|
export const HALOGEN_DEFAULT_BASE_URL = process.env.HALOGEN_BASE_URL || `http://127.0.0.1:${HALOGEN_DEFAULT_PORT}/v1/chat/completions`;
|
|
@@ -58,12 +58,15 @@ export const BUBO_WINDOWS_RELEASE = Object.freeze({
|
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
-
* Каталог штатных моделей
|
|
61
|
+
* Каталог штатных моделей для машинок Halo в `/MODELS/Local` (1 основная GPU-модель + 7 NPU-моделей на одном порту 8731).
|
|
62
62
|
*/
|
|
63
63
|
export const HALOGEN_MODELS_CATALOG = Object.freeze([
|
|
64
64
|
{
|
|
65
65
|
nodeName: 'Qwen3.8-Flash-Next',
|
|
66
|
-
|
|
66
|
+
label: 'Qwen3.8-Flash-Next (180B MoE)',
|
|
67
|
+
icon: 'ai:qwen',
|
|
68
|
+
path: '/MODELS/Local/Qwen3.8-Flash-Next',
|
|
69
|
+
baseUrl: 'http://127.0.0.1:8731/v1/chat/completions',
|
|
67
70
|
model: 'halogen-qwen3.8-flash-next',
|
|
68
71
|
hfRepo: 'peonist-ai/halogen-qwen3.8-flash-next',
|
|
69
72
|
windowsBuboRepo: 'unsloth/Qwen3.8-Flash-Next-GGUF',
|
|
@@ -76,12 +79,16 @@ export const HALOGEN_MODELS_CATALOG = Object.freeze([
|
|
|
76
79
|
ramGb: 62.1,
|
|
77
80
|
kvPoolGb: 27.0,
|
|
78
81
|
lookupTableGb: 47.7,
|
|
82
|
+
efforts: ['off', 'low', 'medium', 'high'],
|
|
79
83
|
capabilities: ['chat', 'stream', 'tools', 'functions', 'vision', 'effort', 'thinking', 'fork'],
|
|
80
84
|
role: 'Основная агентская модель (`ai/config.js → model`): 180B MoE (Linux: Halogen 4.16 bpw / Windows: Halowin Bubo UD-Q4_K_XL + Dense Twin + MTP Draft Head, 57–94 tok/s decode, ~1415–1800 tok/s prefill)',
|
|
81
85
|
},
|
|
82
86
|
{
|
|
83
87
|
nodeName: 'Qwen3.5-2B-NPU',
|
|
84
|
-
|
|
88
|
+
label: 'Qwen3.5-2B (NPU Compact)',
|
|
89
|
+
icon: 'ai:qwen',
|
|
90
|
+
path: '/MODELS/Local/Qwen3.5-2B-NPU',
|
|
91
|
+
baseUrl: 'http://127.0.0.1:8731/v1/chat/completions',
|
|
85
92
|
model: 'qwen3.5-2b',
|
|
86
93
|
hfRepo: 'peonist-ai/halogen-npu-qwen3.5-2b',
|
|
87
94
|
device: 'NPU (Ryzen AI 55 TOPS)',
|
|
@@ -94,7 +101,10 @@ export const HALOGEN_MODELS_CATALOG = Object.freeze([
|
|
|
94
101
|
},
|
|
95
102
|
{
|
|
96
103
|
nodeName: 'Qwen3-ASR-NPU',
|
|
97
|
-
|
|
104
|
+
label: 'Qwen3-ASR 1.7B (NPU STT)',
|
|
105
|
+
icon: 'carbon:microphone',
|
|
106
|
+
path: '/MODELS/Local/Qwen3-ASR-NPU',
|
|
107
|
+
baseUrl: 'http://127.0.0.1:8731/v1/audio/transcriptions',
|
|
98
108
|
model: 'qwen3-asr-1.7b',
|
|
99
109
|
hfRepo: 'peonist-ai/halogen-npu-qwen3-asr-1.7b',
|
|
100
110
|
gufoService: 'asr',
|
|
@@ -108,7 +118,10 @@ export const HALOGEN_MODELS_CATALOG = Object.freeze([
|
|
|
108
118
|
},
|
|
109
119
|
{
|
|
110
120
|
nodeName: 'Qwen3-TTS-NPU',
|
|
111
|
-
|
|
121
|
+
label: 'Qwen3-TTS 1.7B (NPU Voice)',
|
|
122
|
+
icon: 'carbon:volume-up',
|
|
123
|
+
path: '/MODELS/Local/Qwen3-TTS-NPU',
|
|
124
|
+
baseUrl: 'http://127.0.0.1:8731/v1/audio/speech',
|
|
112
125
|
model: 'qwen3-tts-1.7b-customvoice',
|
|
113
126
|
hfRepo: 'peonist-ai/halogen-npu-qwen3-tts-1.7b-customvoice',
|
|
114
127
|
gufoService: 'tts',
|
|
@@ -122,7 +135,10 @@ export const HALOGEN_MODELS_CATALOG = Object.freeze([
|
|
|
122
135
|
},
|
|
123
136
|
{
|
|
124
137
|
nodeName: 'Flux2-Klein-NPU',
|
|
125
|
-
|
|
138
|
+
label: 'FLUX.2-klein 4B (NPU Image)',
|
|
139
|
+
icon: 'ai:stablediffusion',
|
|
140
|
+
path: '/MODELS/Local/Flux2-Klein-NPU',
|
|
141
|
+
baseUrl: 'http://127.0.0.1:8731/v1/images/generations',
|
|
126
142
|
model: 'flux2-klein-4b',
|
|
127
143
|
hfRepo: 'peonist-ai/halogen-npu-flux2-klein-4b',
|
|
128
144
|
gufoService: 'image',
|
|
@@ -136,7 +152,10 @@ export const HALOGEN_MODELS_CATALOG = Object.freeze([
|
|
|
136
152
|
},
|
|
137
153
|
{
|
|
138
154
|
nodeName: 'Qwen3-Embedding-NPU',
|
|
139
|
-
|
|
155
|
+
label: 'Qwen3-Embedding 0.6B (NPU RAG)',
|
|
156
|
+
icon: 'carbon:search-locate',
|
|
157
|
+
path: '/MODELS/Local/Qwen3-Embedding-NPU',
|
|
158
|
+
baseUrl: 'http://127.0.0.1:8731/v1/embeddings',
|
|
140
159
|
model: 'qwen3-embedding-0.6b',
|
|
141
160
|
hfRepo: 'peonist-ai/halogen-npu-qwen3-embedding-0.6b',
|
|
142
161
|
gufoService: 'embedding',
|
|
@@ -150,7 +169,10 @@ export const HALOGEN_MODELS_CATALOG = Object.freeze([
|
|
|
150
169
|
},
|
|
151
170
|
{
|
|
152
171
|
nodeName: 'Qwen3-Reranker-NPU',
|
|
153
|
-
|
|
172
|
+
label: 'Qwen3-Reranker 0.6B (NPU Rerank)',
|
|
173
|
+
icon: 'carbon:filter',
|
|
174
|
+
path: '/MODELS/Local/Qwen3-Reranker-NPU',
|
|
175
|
+
baseUrl: 'http://127.0.0.1:8731/v1/rerank',
|
|
154
176
|
model: 'qwen3-reranker-0.6b',
|
|
155
177
|
hfRepo: 'peonist-ai/halogen-npu-qwen3-reranker-0.6b',
|
|
156
178
|
device: 'NPU (Ryzen AI 55 TOPS)',
|
|
@@ -163,7 +185,10 @@ export const HALOGEN_MODELS_CATALOG = Object.freeze([
|
|
|
163
185
|
},
|
|
164
186
|
{
|
|
165
187
|
nodeName: 'Decider-NPU',
|
|
166
|
-
|
|
188
|
+
label: 'Decider 0.8B (NPU SystemOne)',
|
|
189
|
+
icon: 'carbon:decision-tree',
|
|
190
|
+
path: '/MODELS/Local/Decider-NPU',
|
|
191
|
+
baseUrl: 'http://127.0.0.1:8731/v1/systemone',
|
|
167
192
|
model: 'decider-0.8b',
|
|
168
193
|
hfRepo: 'peonist-ai/halogen-npu-decider-0.8b',
|
|
169
194
|
gufoService: 'decision',
|
|
@@ -431,26 +456,26 @@ export function computeHalogenMemoryPlan({
|
|
|
431
456
|
// Нативная подготовка и запуск Halowin Bubo (gufo v0.26.0 + AMD ROCm 10.0.0 gfx1151 + Ryzen AI XRT NPU) на Windows 11
|
|
432
457
|
const windowsPrepareCommand = [
|
|
433
458
|
`.\\bubo.exe prepare runtime --yes`,
|
|
434
|
-
`.\\bubo.exe prepare model --model-set original --output "MODELS\\
|
|
459
|
+
`.\\bubo.exe prepare model --model-set original --output "MODELS\\Local" --yes`,
|
|
435
460
|
].join('\n');
|
|
436
461
|
|
|
437
462
|
const windowsCommand = [
|
|
438
463
|
`bubo.exe serve llm ^`,
|
|
439
464
|
` --host 127.0.0.1 --port ${HALOGEN_DEFAULT_PORT} ^`,
|
|
440
|
-
` --manifest "MODELS\\
|
|
465
|
+
` --manifest "MODELS\\Local\\models\\qwen38-flash-next-base-ud-q4-k-xl-v1\\bubo-package.json" ^`,
|
|
441
466
|
` --served-model-name halogen-qwen3.8-flash-next ^`,
|
|
442
467
|
` --profile ${kvSlots > 1 ? 'subagents' : 'default'} --context ${contextTokens} ^`,
|
|
443
468
|
` --npu-prefill on ^`,
|
|
444
|
-
` --cache-disk "MODELS\\
|
|
469
|
+
` --cache-disk "MODELS\\Local\\#cache\\disk"`,
|
|
445
470
|
].join('\n');
|
|
446
471
|
|
|
447
472
|
const windowsGufoServicesCommand = [
|
|
448
|
-
`gufo.exe serve --host 127.0.0.1 --port ${HALOGEN_DEFAULT_PORT} llm --model "MODELS\\
|
|
449
|
-
`gufo.exe serve asr --model "MODELS\\
|
|
450
|
-
`gufo.exe serve tts --model "MODELS\\
|
|
451
|
-
`gufo.exe serve embedding --model "MODELS\\
|
|
452
|
-
`gufo.exe serve image --model "MODELS\\
|
|
453
|
-
`gufo.exe serve decision --model "MODELS\\
|
|
473
|
+
`gufo.exe serve --host 127.0.0.1 --port ${HALOGEN_DEFAULT_PORT} llm --model "MODELS\\Local\\Qwen3.8-Flash-Next\\#weights\\Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf" --served-model-name halogen-qwen3.8-flash-next --speculative mtp`,
|
|
474
|
+
`gufo.exe serve asr --model "MODELS\\Local\\Qwen3-ASR-NPU\\#weights"`,
|
|
475
|
+
`gufo.exe serve tts --model "MODELS\\Local\\Qwen3-TTS-NPU\\#weights"`,
|
|
476
|
+
`gufo.exe serve embedding --model "MODELS\\Local\\Qwen3-Embedding-NPU\\#weights"`,
|
|
477
|
+
`gufo.exe serve image --model "MODELS\\Local\\Flux2-Klein-NPU\\#weights"`,
|
|
478
|
+
`gufo.exe serve decision --model "MODELS\\Local\\Decider-NPU\\#weights"`,
|
|
454
479
|
].join('\n');
|
|
455
480
|
|
|
456
481
|
const windowsStrataCommand = `strata launch --family qwen --model iq3_s --port ${HALOGEN_DEFAULT_PORT}`;
|
|
@@ -506,6 +531,8 @@ export function inspectWindowsHalogenEnvironment({ rootDir = process.cwd() } = {
|
|
|
506
531
|
const systemRoot = process.env.SystemRoot || 'C:\\Windows';
|
|
507
532
|
|
|
508
533
|
const buboCandidates = [
|
|
534
|
+
path.join(rootDir, 'MODELS', 'Local', '#runtime', 'bubo', 'bin', 'bubo.exe'),
|
|
535
|
+
path.join(rootDir, 'MODELS', 'Local', '#runtime', 'bubo', 'bubo.exe'),
|
|
509
536
|
path.join(rootDir, 'MODELS', 'Halogen', '#runtime', 'bubo', 'bin', 'bubo.exe'),
|
|
510
537
|
path.join(rootDir, 'MODELS', 'Halogen', '#runtime', 'bubo', 'bubo.exe'),
|
|
511
538
|
path.join(localAppData, 'Bubo', 'bin', 'bubo.exe'),
|
|
@@ -516,10 +543,11 @@ export function inspectWindowsHalogenEnvironment({ rootDir = process.cwd() } = {
|
|
|
516
543
|
? (path.basename(path.dirname(buboExePath)).toLowerCase() === 'bin'
|
|
517
544
|
? path.dirname(path.dirname(buboExePath))
|
|
518
545
|
: path.dirname(buboExePath))
|
|
519
|
-
: path.join(rootDir, 'MODELS', '
|
|
546
|
+
: path.join(rootDir, 'MODELS', 'Local', '#runtime', 'bubo');
|
|
520
547
|
|
|
521
548
|
const rocmCandidates = [
|
|
522
549
|
process.env.BUBO_RUNTIME_DIR || '',
|
|
550
|
+
path.join(rootDir, 'MODELS', 'Local', '#runtime', 'bubo', 'runtime', 'rocm-10.0.0-gfx1151'),
|
|
523
551
|
path.join(rootDir, 'MODELS', 'Halogen', '#runtime', 'bubo', 'runtime', 'rocm-10.0.0-gfx1151'),
|
|
524
552
|
path.join(localAppData, 'Bubo', 'runtime', 'rocm-10.0.0-gfx1151'),
|
|
525
553
|
].filter(Boolean);
|
|
@@ -527,6 +555,9 @@ export function inspectWindowsHalogenEnvironment({ rootDir = process.cwd() } = {
|
|
|
527
555
|
|
|
528
556
|
const manifestCandidates = [
|
|
529
557
|
process.env.BUBO_MANIFEST || '',
|
|
558
|
+
path.join(rootDir, 'MODELS', 'Local', 'models', 'qwen38-flash-next-base-ud-q4-k-xl-v1', 'bubo-package.json'),
|
|
559
|
+
path.join(rootDir, 'MODELS', 'Local', 'models', 'huihui-qwen38-flash-next-ud-q4-k-xl-v1', 'bubo-package.json'),
|
|
560
|
+
path.join(rootDir, 'MODELS', 'Local', 'Qwen3.8-Flash-Next', '#weights', 'bubo-package.json'),
|
|
530
561
|
path.join(rootDir, 'MODELS', 'Halogen', 'models', 'qwen38-flash-next-base-ud-q4-k-xl-v1', 'bubo-package.json'),
|
|
531
562
|
path.join(rootDir, 'MODELS', 'Halogen', 'models', 'huihui-qwen38-flash-next-ud-q4-k-xl-v1', 'bubo-package.json'),
|
|
532
563
|
path.join(rootDir, 'MODELS', 'Halogen', 'Qwen3.8-Flash-Next', '#weights', 'bubo-package.json'),
|
|
@@ -536,6 +567,8 @@ export function inspectWindowsHalogenEnvironment({ rootDir = process.cwd() } = {
|
|
|
536
567
|
const buboManifestPath = manifestCandidates.find(p => fs.existsSync(p)) || null;
|
|
537
568
|
|
|
538
569
|
const gufoCandidates = [
|
|
570
|
+
path.join(rootDir, 'MODELS', 'Local', '#runtime', 'gufo', 'gufo.exe'),
|
|
571
|
+
path.join(rootDir, 'MODELS', 'Local', '#runtime', 'bubo', 'bin', 'gufo.exe'),
|
|
539
572
|
path.join(rootDir, 'MODELS', 'Halogen', '#runtime', 'gufo', 'gufo.exe'),
|
|
540
573
|
path.join(rootDir, 'MODELS', 'Halogen', '#runtime', 'bubo', 'bin', 'gufo.exe'),
|
|
541
574
|
path.join(localAppData, 'Gufo', 'gufo.exe'),
|
|
@@ -545,6 +578,7 @@ export function inspectWindowsHalogenEnvironment({ rootDir = process.cwd() } = {
|
|
|
545
578
|
const strataCandidates = [
|
|
546
579
|
path.join(localAppData, 'Programs', 'Strata', 'strata.exe'),
|
|
547
580
|
path.join(localAppData, 'Strata', 'strata.exe'),
|
|
581
|
+
path.join(rootDir, 'MODELS', 'Local', '#runtime', 'strata', 'strata.exe'),
|
|
548
582
|
path.join(rootDir, 'MODELS', 'Halogen', '#runtime', 'strata', 'strata.exe'),
|
|
549
583
|
];
|
|
550
584
|
const strataExePath = strataCandidates.find(p => fs.existsSync(p)) || null;
|
|
@@ -741,9 +775,10 @@ export async function probeHalogenServer(baseUrl = HALOGEN_DEFAULT_BASE_URL, { f
|
|
|
741
775
|
*/
|
|
742
776
|
function findLocalGgufModel(rootDir = process.cwd()) {
|
|
743
777
|
const searchRoots = [
|
|
778
|
+
path.join(rootDir, 'MODELS', 'Local', 'models'),
|
|
779
|
+
path.join(rootDir, 'MODELS', 'Local'),
|
|
744
780
|
path.join(rootDir, 'MODELS', 'Halogen', 'models'),
|
|
745
781
|
path.join(rootDir, 'MODELS', 'Halogen'),
|
|
746
|
-
path.join(rootDir, 'MODELS', 'Local'),
|
|
747
782
|
];
|
|
748
783
|
for (const base of searchRoots) {
|
|
749
784
|
if (!fs.existsSync(base))
|
|
@@ -783,10 +818,10 @@ export async function prepareWindowsBuboRuntime({
|
|
|
783
818
|
fetchImpl = globalThis.fetch,
|
|
784
819
|
spawnImpl = spawn,
|
|
785
820
|
} = {}) {
|
|
786
|
-
const runtimeRoot = path.join(rootDir, 'MODELS', '
|
|
821
|
+
const runtimeRoot = path.join(rootDir, 'MODELS', 'Local', '#runtime');
|
|
787
822
|
const buboDir = path.join(runtimeRoot, 'bubo');
|
|
788
823
|
const rocmDir = path.join(buboDir, 'runtime', 'rocm-10.0.0-gfx1151');
|
|
789
|
-
const modelsOutDir = path.join(rootDir, 'MODELS', '
|
|
824
|
+
const modelsOutDir = path.join(rootDir, 'MODELS', 'Local');
|
|
790
825
|
|
|
791
826
|
await fsp.mkdir(runtimeRoot, { recursive: true });
|
|
792
827
|
|
|
@@ -960,7 +995,7 @@ export async function startHalogenBackend({
|
|
|
960
995
|
}
|
|
961
996
|
|
|
962
997
|
const winEnv = inspectWindowsHalogenEnvironment({ rootDir });
|
|
963
|
-
const cacheDir = path.join(rootDir, 'MODELS', '
|
|
998
|
+
const cacheDir = path.join(rootDir, 'MODELS', 'Local', '#cache');
|
|
964
999
|
fs.mkdirSync(path.join(cacheDir, 'disk'), { recursive: true });
|
|
965
1000
|
fs.mkdirSync(path.join(cacheDir, 'slots'), { recursive: true });
|
|
966
1001
|
|
|
@@ -1294,12 +1329,72 @@ export function computeFallbackDecision({ state = '', questions = {}, model = 'd
|
|
|
1294
1329
|
}
|
|
1295
1330
|
|
|
1296
1331
|
/**
|
|
1297
|
-
*
|
|
1332
|
+
* Зарегистрировать модели Halo (`HALOGEN_MODELS_CATALOG`) как дочерние узлы `$ai` внутри `/MODELS/Local/<nodeName>`.
|
|
1333
|
+
*/
|
|
1334
|
+
export async function ensureHalogenModelNodes(rootDir = process.cwd(), { models = null, onlyWithWeights = false } = {}) {
|
|
1335
|
+
let filterList = null;
|
|
1336
|
+
if (Array.isArray(models) && models.length) {
|
|
1337
|
+
filterList = new Set(models.map(m => String(m).trim()));
|
|
1338
|
+
}
|
|
1339
|
+
else if (typeof models === 'string' && models.trim()) {
|
|
1340
|
+
filterList = new Set(models.split(',').map(m => m.trim()).filter(Boolean));
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
const targets = filterList
|
|
1344
|
+
? HALOGEN_MODELS_CATALOG.filter(m =>
|
|
1345
|
+
filterList.has(m.nodeName) || filterList.has(m.model) || filterList.has(m.hfRepo) || filterList.has(m.path)
|
|
1346
|
+
)
|
|
1347
|
+
: [...HALOGEN_MODELS_CATALOG];
|
|
1348
|
+
|
|
1349
|
+
const registered = [];
|
|
1350
|
+
for (const spec of targets) {
|
|
1351
|
+
const weightsDir = path.join(rootDir, 'MODELS', 'Local', spec.nodeName, '#weights');
|
|
1352
|
+
if (onlyWithWeights) {
|
|
1353
|
+
let hasFiles = false;
|
|
1354
|
+
try {
|
|
1355
|
+
if (fs.existsSync(weightsDir)) {
|
|
1356
|
+
hasFiles = fs.readdirSync(weightsDir).some(f => !f.endsWith('.part') && f !== 'hf-manifest.json');
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
catch { /* ignore */ }
|
|
1360
|
+
if (!hasFiles)
|
|
1361
|
+
continue;
|
|
1362
|
+
}
|
|
1363
|
+
const res = await registerLocalModelNode({
|
|
1364
|
+
rootDir,
|
|
1365
|
+
folderName: spec.nodeName,
|
|
1366
|
+
label: spec.label || spec.nodeName,
|
|
1367
|
+
repoId: spec.hfRepo,
|
|
1368
|
+
model: spec.model,
|
|
1369
|
+
protocol: 'halogen',
|
|
1370
|
+
icon: spec.icon || 'ai:qwen',
|
|
1371
|
+
modality: spec.modality,
|
|
1372
|
+
capabilities: spec.capabilities,
|
|
1373
|
+
efforts: spec.efforts,
|
|
1374
|
+
maxTokens: spec.maxTokens,
|
|
1375
|
+
maxOutput: spec.maxOutput,
|
|
1376
|
+
baseUrl: spec.baseUrl || HALOGEN_DEFAULT_BASE_URL,
|
|
1377
|
+
weightsDir,
|
|
1378
|
+
});
|
|
1379
|
+
registered.push(res.modelPath);
|
|
1380
|
+
}
|
|
1381
|
+
try {
|
|
1382
|
+
const localNode = await globalThis.WORK?.get_item?.('/MODELS/Local');
|
|
1383
|
+
localNode?.reset?.();
|
|
1384
|
+
}
|
|
1385
|
+
catch { /* ignore */ }
|
|
1386
|
+
return registered;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
/**
|
|
1390
|
+
* Проверить наличие скачанных весов и узлов `$ai` для каждой модели из `HALOGEN_MODELS_CATALOG` в `MODELS/Local/<nodeName>/#weights`.
|
|
1298
1391
|
*/
|
|
1299
1392
|
export function inspectHalogenCatalogWithStatus(rootDir = process.cwd()) {
|
|
1300
1393
|
const activeDownloads = listHuggingFaceDownloads();
|
|
1301
1394
|
return HALOGEN_MODELS_CATALOG.map(spec => {
|
|
1302
|
-
const
|
|
1395
|
+
const localWeightsDir = path.join(rootDir, 'MODELS', 'Local', spec.nodeName, '#weights');
|
|
1396
|
+
const legacyWeightsDir = path.join(rootDir, 'MODELS', 'Halogen', spec.nodeName, '#weights');
|
|
1397
|
+
const weightsDir = fs.existsSync(localWeightsDir) || !fs.existsSync(legacyWeightsDir) ? localWeightsDir : legacyWeightsDir;
|
|
1303
1398
|
let downloadedFiles = [];
|
|
1304
1399
|
if (fs.existsSync(weightsDir)) {
|
|
1305
1400
|
try {
|
|
@@ -1308,12 +1403,14 @@ export function inspectHalogenCatalogWithStatus(rootDir = process.cwd()) {
|
|
|
1308
1403
|
}
|
|
1309
1404
|
catch { /* ignore */ }
|
|
1310
1405
|
}
|
|
1406
|
+
const nodeConfigured = fs.existsSync(path.join(rootDir, 'MODELS', 'Local', spec.nodeName, '$ai', 'class.js'));
|
|
1311
1407
|
const activeJob = activeDownloads.find(d =>
|
|
1312
1408
|
d.status === 'downloading'
|
|
1313
1409
|
&& (d.repoId === spec.hfRepo || d.folderName === spec.nodeName || d.modelPath === spec.path)
|
|
1314
1410
|
) || null;
|
|
1315
1411
|
return {
|
|
1316
1412
|
...spec,
|
|
1413
|
+
nodeConfigured,
|
|
1317
1414
|
weightsDir: path.relative(rootDir, weightsDir).replace(/\\/g, '/'),
|
|
1318
1415
|
weightsDownloaded: downloadedFiles.length > 0,
|
|
1319
1416
|
downloadedFiles,
|
|
@@ -1323,8 +1420,9 @@ export function inspectHalogenCatalogWithStatus(rootDir = process.cwd()) {
|
|
|
1323
1420
|
}
|
|
1324
1421
|
|
|
1325
1422
|
/**
|
|
1326
|
-
* Поставить в очередь скачивание всего пакета моделей `/MODELS/
|
|
1327
|
-
* или выбранного подмножества моделей с Hugging Face Hub прямо в `MODELS/
|
|
1423
|
+
* Поставить в очередь скачивание всего пакета моделей Halo в `/MODELS/Local` (1 основная GPU-модель + 7 NPU-моделей)
|
|
1424
|
+
* или выбранного подмножества моделей с Hugging Face Hub прямо в `MODELS/Local/<nodeName>/#weights`,
|
|
1425
|
+
* регистрируя узлы `$ai` в `/MODELS/Local/<nodeName>`.
|
|
1328
1426
|
*/
|
|
1329
1427
|
export async function downloadHalogenBundle({
|
|
1330
1428
|
rootDir = process.cwd(),
|
|
@@ -1353,6 +1451,7 @@ export async function downloadHalogenBundle({
|
|
|
1353
1451
|
let alreadyActiveCount = 0;
|
|
1354
1452
|
|
|
1355
1453
|
for (const spec of targets) {
|
|
1454
|
+
await ensureHalogenModelNodes(rootDir, { models: [spec.nodeName], onlyWithWeights: false });
|
|
1356
1455
|
const running = existingDownloads.find(d =>
|
|
1357
1456
|
d.status === 'downloading'
|
|
1358
1457
|
&& (d.repoId === spec.hfRepo || d.folderName === spec.nodeName || d.modelPath === spec.path)
|
|
@@ -1364,7 +1463,7 @@ export async function downloadHalogenBundle({
|
|
|
1364
1463
|
}
|
|
1365
1464
|
const job = await startHuggingFaceDownload({
|
|
1366
1465
|
repoId: spec.hfRepo,
|
|
1367
|
-
providerFolder: '
|
|
1466
|
+
providerFolder: 'Local',
|
|
1368
1467
|
targetNodeName: spec.nodeName,
|
|
1369
1468
|
rootDir,
|
|
1370
1469
|
token,
|
|
@@ -939,14 +939,19 @@ export async function registerLocalModelNode({
|
|
|
939
939
|
folderName,
|
|
940
940
|
label,
|
|
941
941
|
repoId,
|
|
942
|
+
model = '',
|
|
943
|
+
protocol = '',
|
|
944
|
+
icon: customIcon = '',
|
|
942
945
|
modality = 'llm',
|
|
943
946
|
capabilities = null,
|
|
947
|
+
efforts = null,
|
|
944
948
|
weightsDir,
|
|
945
949
|
primaryFile = '',
|
|
946
950
|
baseModel = '',
|
|
947
951
|
adapterPath = '',
|
|
948
952
|
maxTokens = 16384,
|
|
949
|
-
|
|
953
|
+
maxOutput = 8192,
|
|
954
|
+
baseUrl = 'http://127.0.0.1:8731/v1/chat/completions',
|
|
950
955
|
} = {}) {
|
|
951
956
|
const safeName = String(folderName || safeModelFolderName(repoId || label || 'Local-Model')).replace(/[<>:"/\\|?*]/g, '-');
|
|
952
957
|
const aiDir = path.join(rootDir, 'MODELS', 'Local', safeName, '$ai');
|
|
@@ -963,19 +968,20 @@ export async function registerLocalModelNode({
|
|
|
963
968
|
...(rawCaps.includes('chat') && !rawCaps.includes('stream') ? ['stream'] : []),
|
|
964
969
|
...(rawCaps.includes('tools') && !rawCaps.includes('functions') ? ['functions'] : []),
|
|
965
970
|
])];
|
|
966
|
-
const icon = resolveModelIcon({ repoId: repoId || safeName, modality, capabilities: resolvedCaps });
|
|
971
|
+
const icon = customIcon || resolveModelIcon({ repoId: repoId || safeName, modality, capabilities: resolvedCaps });
|
|
967
972
|
|
|
968
973
|
const meta = {
|
|
969
974
|
label: String(label || safeName),
|
|
970
975
|
icon,
|
|
971
|
-
protocol: String(primaryFile || '').endsWith('.gguf') ? 'openai' : 'local',
|
|
976
|
+
protocol: protocol || (String(primaryFile || '').endsWith('.gguf') ? 'openai' : 'local'),
|
|
972
977
|
baseUrl,
|
|
973
|
-
model: repoId || safeName,
|
|
978
|
+
model: model || repoId || safeName,
|
|
974
979
|
modality,
|
|
975
980
|
maxTokens: Number(maxTokens) || 16384,
|
|
976
|
-
maxOutput: 8192,
|
|
981
|
+
maxOutput: Number(maxOutput) || 8192,
|
|
977
982
|
functionCalling: resolvedCaps.includes('tools') || resolvedCaps.includes('functions'),
|
|
978
983
|
capabilities: resolvedCaps,
|
|
984
|
+
...(Array.isArray(efforts) && efforts.length ? { efforts } : {}),
|
|
979
985
|
hfRepo: repoId || '',
|
|
980
986
|
weightsDir: weightsDir ? path.relative(rootDir, weightsDir).replace(/\\/g, '/') : '',
|
|
981
987
|
primaryFile: primaryFile || '',
|
|
@@ -54,9 +54,22 @@ import {
|
|
|
54
54
|
computeFallbackEmbeddings,
|
|
55
55
|
computeFallbackRerank,
|
|
56
56
|
computeFallbackDecision,
|
|
57
|
+
ensureHalogenModelNodes,
|
|
57
58
|
inspectHalogenCatalogWithStatus,
|
|
58
59
|
downloadHalogenBundle,
|
|
59
60
|
} from './halogen.js';
|
|
61
|
+
import {
|
|
62
|
+
AI_PROVIDER_PRESETS,
|
|
63
|
+
findProviderPreset,
|
|
64
|
+
buildProviderClassScripts,
|
|
65
|
+
buildModelClassScript,
|
|
66
|
+
scaffoldProviderOnDiskSync,
|
|
67
|
+
evaluateAiNodeActivitySync,
|
|
68
|
+
getProviderStatusSync,
|
|
69
|
+
saveProviderConfigSync,
|
|
70
|
+
addProviderModelsSync,
|
|
71
|
+
toggleAiNodeEnabledSync,
|
|
72
|
+
} from './providers.js';
|
|
60
73
|
|
|
61
74
|
export const halogenFallback = Object.freeze({
|
|
62
75
|
normalizeHalogenTtsPayload,
|
|
@@ -71,12 +84,23 @@ export const halogenFallback = Object.freeze({
|
|
|
71
84
|
computeHalogenMemoryPlan,
|
|
72
85
|
inspectWindowsHalogenEnvironment,
|
|
73
86
|
prepareWindowsBuboRuntime,
|
|
87
|
+
ensureHalogenModelNodes,
|
|
74
88
|
inspectHalogenCatalogWithStatus,
|
|
75
89
|
downloadHalogenBundle,
|
|
76
90
|
});
|
|
77
91
|
|
|
78
92
|
export {
|
|
79
93
|
localStreamChat,
|
|
94
|
+
AI_PROVIDER_PRESETS,
|
|
95
|
+
findProviderPreset,
|
|
96
|
+
buildProviderClassScripts,
|
|
97
|
+
buildModelClassScript,
|
|
98
|
+
scaffoldProviderOnDiskSync,
|
|
99
|
+
evaluateAiNodeActivitySync,
|
|
100
|
+
getProviderStatusSync,
|
|
101
|
+
saveProviderConfigSync,
|
|
102
|
+
addProviderModelsSync,
|
|
103
|
+
toggleAiNodeEnabledSync,
|
|
80
104
|
HALOGEN_DEFAULT_PORT,
|
|
81
105
|
HALOGEN_DEFAULT_BASE_URL,
|
|
82
106
|
HALOGEN_IMAGE_TAG,
|
|
@@ -97,6 +121,7 @@ export {
|
|
|
97
121
|
computeFallbackEmbeddings,
|
|
98
122
|
computeFallbackRerank,
|
|
99
123
|
computeFallbackDecision,
|
|
124
|
+
ensureHalogenModelNodes,
|
|
100
125
|
inspectHalogenCatalogWithStatus,
|
|
101
126
|
downloadHalogenBundle,
|
|
102
127
|
CURATED_HF_MODELS,
|
|
@@ -201,8 +226,116 @@ export const TRAINER_METHODS = Object.freeze({
|
|
|
201
226
|
waitForCompletion: p.wait === true || p.wait === 'true',
|
|
202
227
|
});
|
|
203
228
|
try {
|
|
204
|
-
const
|
|
205
|
-
|
|
229
|
+
const localNode = await globalThis.WORK?.get_item?.('/MODELS/Local');
|
|
230
|
+
localNode?.reset?.();
|
|
231
|
+
const modelsRoot = await globalThis.WORK?.get_item?.('/MODELS');
|
|
232
|
+
modelsRoot?.reset?.();
|
|
233
|
+
}
|
|
234
|
+
catch { /* ignore */ }
|
|
235
|
+
return res;
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
provider_presets: {
|
|
239
|
+
level: 'read',
|
|
240
|
+
fn: async function () {
|
|
241
|
+
return { items: AI_PROVIDER_PRESETS };
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
provider_status: {
|
|
245
|
+
level: 'read',
|
|
246
|
+
fn: async function () {
|
|
247
|
+
await this?.init;
|
|
248
|
+
return getProviderStatusSync(this);
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
provider_save_config: {
|
|
252
|
+
level: 'admin',
|
|
253
|
+
fn: async function (p = {}) {
|
|
254
|
+
await this?.init;
|
|
255
|
+
const itemPath = String(this?.path || this?.short || '');
|
|
256
|
+
const parts = itemPath.split('/').filter(Boolean);
|
|
257
|
+
const providerId = p.providerId || parts[1] || this?.id || '';
|
|
258
|
+
const res = saveProviderConfigSync({
|
|
259
|
+
providerId,
|
|
260
|
+
apiKey: p.apiKey,
|
|
261
|
+
folderId: p.folderId,
|
|
262
|
+
baseUrl: p.baseUrl,
|
|
263
|
+
authUrl: p.authUrl,
|
|
264
|
+
scope: p.scope,
|
|
265
|
+
protocol: p.protocol,
|
|
266
|
+
enabled: p.enabled !== undefined ? (p.enabled !== false && p.enabled !== 'false') : undefined,
|
|
267
|
+
});
|
|
268
|
+
try {
|
|
269
|
+
const provNode = await globalThis.WORK?.get_item?.(`/MODELS/${providerId}`);
|
|
270
|
+
if (provNode) {
|
|
271
|
+
provNode.reset?.();
|
|
272
|
+
provNode.DATA = await provNode.import?.();
|
|
273
|
+
const children = await provNode.children;
|
|
274
|
+
if (Array.isArray(children)) {
|
|
275
|
+
for (const ch of children) {
|
|
276
|
+
ch.reset?.();
|
|
277
|
+
ch.DATA = await ch.import?.();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const modelsRoot = await globalThis.WORK?.get_item?.('/MODELS');
|
|
282
|
+
modelsRoot?.reset?.();
|
|
283
|
+
}
|
|
284
|
+
catch { /* ignore */ }
|
|
285
|
+
return {
|
|
286
|
+
...res,
|
|
287
|
+
status: getProviderStatusSync(this),
|
|
288
|
+
};
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
provider_add_models: {
|
|
292
|
+
level: 'admin',
|
|
293
|
+
fn: async function (p = {}) {
|
|
294
|
+
await this?.init;
|
|
295
|
+
const itemPath = String(this?.path || this?.short || '');
|
|
296
|
+
const parts = itemPath.split('/').filter(Boolean);
|
|
297
|
+
const providerId = p.providerId || parts[1] || this?.id || '';
|
|
298
|
+
let models = p.models || p.model || [];
|
|
299
|
+
if (typeof models === 'string' && models.trim().startsWith('[')) {
|
|
300
|
+
try { models = JSON.parse(models); } catch { /* ignore */ }
|
|
301
|
+
}
|
|
302
|
+
else if (typeof models === 'string') {
|
|
303
|
+
models = models.split(',').map(s => s.trim()).filter(Boolean);
|
|
304
|
+
}
|
|
305
|
+
const res = addProviderModelsSync({
|
|
306
|
+
providerId,
|
|
307
|
+
models,
|
|
308
|
+
});
|
|
309
|
+
try {
|
|
310
|
+
const provNode = await globalThis.WORK?.get_item?.(`/MODELS/${providerId}`);
|
|
311
|
+
provNode?.reset?.();
|
|
312
|
+
const modelsRoot = await globalThis.WORK?.get_item?.('/MODELS');
|
|
313
|
+
modelsRoot?.reset?.();
|
|
314
|
+
}
|
|
315
|
+
catch { /* ignore */ }
|
|
316
|
+
return {
|
|
317
|
+
...res,
|
|
318
|
+
status: getProviderStatusSync(this),
|
|
319
|
+
};
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
provider_toggle_model: {
|
|
323
|
+
level: 'admin',
|
|
324
|
+
fn: async function (p = {}) {
|
|
325
|
+
const nodePath = p.nodePath || p.path || this?.path || '';
|
|
326
|
+
const enabled = p.enabled !== false && p.enabled !== 'false';
|
|
327
|
+
const res = toggleAiNodeEnabledSync({ nodePath, enabled });
|
|
328
|
+
try {
|
|
329
|
+
const node = await globalThis.WORK?.get_item?.(res.path);
|
|
330
|
+
if (node) {
|
|
331
|
+
node.reset?.();
|
|
332
|
+
node.DATA = await node.import?.();
|
|
333
|
+
}
|
|
334
|
+
const parts = res.path.split('/').filter(Boolean);
|
|
335
|
+
if (parts[1]) {
|
|
336
|
+
const provNode = await globalThis.WORK?.get_item?.(`/MODELS/${parts[1]}`);
|
|
337
|
+
provNode?.reset?.();
|
|
338
|
+
}
|
|
206
339
|
}
|
|
207
340
|
catch { /* ignore */ }
|
|
208
341
|
return res;
|