odant-work 1.0.15 → 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.
Files changed (78) hide show
  1. package/#system/package-manifest.json +51 -43
  2. package/$server/$folder/handlers/methods/create/$handler/class.js +83 -2
  3. package/$server/$folder/lib/icon/icon.js +14 -0
  4. package/$server/$folder/lib/node/node.js +51 -1
  5. package/$server/$folder/lib/tree/tree.js +5 -1
  6. package/.secretsignore +4 -0
  7. package/MODELS/$ai/$folder/$class/$ai/class.js +57 -2
  8. package/MODELS/$ai/$folder/$class/$provider/class.js +1 -1
  9. package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/provider/$handler/class.js +13 -0
  10. package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/provider/$handler/provider.js +479 -0
  11. package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/trainer/$handler/trainer.js +3 -3
  12. package/MODELS/GigaChat/$provider/$folder/$class/$ai/class.js +10 -20
  13. package/MODELS/GigaChat/$provider/class.js +10 -7
  14. package/MODELS/GigaChat/GigaChat 2 Lite/$ai/class.js +15 -0
  15. package/MODELS/GigaChat/GigaChat 2 Max/$ai/class.js +16 -0
  16. package/MODELS/GigaChat/GigaChat 2 Pro/$ai/class.js +15 -0
  17. package/MODELS/GigaChat/GigaChat Embeddings/$ai/class.js +13 -0
  18. package/MODELS/Local/$provider/$folder/$class/$ai/class.js +3 -3
  19. package/MODELS/Local/$provider/class.js +7 -3
  20. package/MODELS/MTS-AI/$provider/$folder/$class/$ai/class.js +8 -0
  21. package/MODELS/MTS-AI/$provider/class.js +8 -0
  22. package/MODELS/MTS-AI/Cotype Lite/$ai/class.js +15 -0
  23. package/MODELS/MTS-AI/Cotype Pro 2/$ai/class.js +15 -0
  24. package/MODELS/MTS-AI/Cotype Vision/$ai/class.js +14 -0
  25. package/MODELS/T-Bank/$provider/$folder/$class/$ai/class.js +8 -0
  26. package/MODELS/T-Bank/$provider/class.js +8 -0
  27. package/MODELS/T-Bank/T-Lite 2.0/$ai/class.js +15 -0
  28. package/MODELS/T-Bank/T-Pro 2.0/$ai/class.js +16 -0
  29. package/MODELS/T-Bank/T-VoiceKit ASR/$ai/class.js +13 -0
  30. package/MODELS/T-Bank/T-VoiceKit TTS/$ai/class.js +13 -0
  31. package/MODELS/Yandex/$provider/$folder/$class/$ai/class.js +9 -0
  32. package/MODELS/Yandex/$provider/class.js +9 -0
  33. package/MODELS/Yandex/Alice AI (YandexGPT 5 Pro)/$ai/class.js +15 -0
  34. package/MODELS/Yandex/Yandex Embeddings/$ai/class.js +13 -0
  35. package/MODELS/Yandex/Yandex SpeechKit ASR/$ai/class.js +13 -0
  36. package/MODELS/Yandex/Yandex SpeechKit TTS/$ai/class.js +13 -0
  37. package/MODELS/Yandex/YandexART/$ai/class.js +13 -0
  38. package/MODELS/Yandex/YandexGPT 5 Lite/$ai/class.js +15 -0
  39. package/MODELS/Yandex/YandexGPT Reasoning/$ai/class.js +22 -0
  40. package/oda/tools/icons/lib/svg/ai.svg +20 -0
  41. package/package.json +1 -1
  42. package/scripts/check-ignored-code.mjs +3 -0
  43. package/scripts/check-secrets.mjs +1 -1
  44. package/sources/data/progress.md +14 -0
  45. package/sources/host/watch.js +13 -2
  46. package/sources/modules/agent/index.js +23 -2
  47. package/sources/modules/trainer/halogen.js +128 -29
  48. package/sources/modules/trainer/huggingface.js +11 -5
  49. package/sources/modules/trainer/index.js +135 -2
  50. package/sources/modules/trainer/providers.js +1213 -0
  51. package/sources/server/class.js +55 -3
  52. package/MODELS/GigaChat/GigaChat Light/$ai/class.js +0 -15
  53. package/MODELS/GigaChat/GigaChat Pro/$ai/class.js +0 -15
  54. package/MODELS/Halogen/$provider/$folder/$class/$ai/class.js +0 -11
  55. package/MODELS/Halogen/$provider/class.js +0 -16
  56. package/MODELS/Halogen/$provider/readme.md +0 -28
  57. package/MODELS/Halogen/Decider-NPU/$ai/class.js +0 -22
  58. package/MODELS/Halogen/Flux2-Klein-NPU/$ai/class.js +0 -21
  59. package/MODELS/Halogen/Qwen3-ASR-NPU/$ai/class.js +0 -21
  60. package/MODELS/Halogen/Qwen3-Embedding-NPU/$ai/class.js +0 -22
  61. package/MODELS/Halogen/Qwen3-Reranker-NPU/$ai/class.js +0 -21
  62. package/MODELS/Halogen/Qwen3-TTS-NPU/$ai/class.js +0 -26
  63. package/MODELS/Halogen/Qwen3.5-2B-NPU/$ai/class.js +0 -23
  64. package/MODELS/Halogen/Qwen3.8-Flash-Next/$ai/class.js +0 -31
  65. package/MODELS/Local/Flux-Image/$ai/class.js +0 -20
  66. package/MODELS/Local/Qwen3-0.6B/$ai/class.js +0 -26
  67. package/MODELS/Local/Qwen3-14B/$ai/class.js +0 -27
  68. package/MODELS/Local/Qwen3-30B-A3B/$ai/class.js +0 -30
  69. package/MODELS/Local/Qwen3-Embedding/$ai/class.js +0 -21
  70. package/MODELS/Local/Qwen3-Voice/$ai/class.js +0 -26
  71. package/MODELS/odant/$provider/$folder/$class/$ai/class.js +0 -11
  72. package/MODELS/odant/$provider/class.js +0 -75
  73. package/MODELS/odant/$provider/readme.md +0 -21
  74. package/MODELS/odant/Qwen3-ASR/$ai/class.js +0 -15
  75. package/MODELS/odant/Qwen3-TTS/$ai/class.js +0 -22
  76. package/MODELS/odant/Qwen3.8 27b/$ai/class.js +0 -11
  77. package/MODELS/odant/Z-Image-Turbo/$ai/class.js +0 -8
  78. package/MODELS/odant/Z-Image-Turbo/$ai/readme.md +0 -24
@@ -0,0 +1,479 @@
1
+ /**
2
+ * form/provider — экран настройки внешнего/облачного провайдера ИИ ($provider):
3
+ * - ввод и сохранение API-ключа / OAuth-токена / Folder ID в #secret без редактирования JSON;
4
+ * - индикация активности провайдера и проверка связи с API (GET /v1/models);
5
+ * - включение/выключение моделей и добавление моделей в 1 клик (из пресета, из /v1/models или вручную).
6
+ */
7
+ export default {
8
+ imports: 'oda//button, oda//icon',
9
+ template: /*html*/`
10
+ <style>
11
+ :host {
12
+ @apply --vertical;
13
+ @apply --flex;
14
+ overflow-y: auto;
15
+ background: var(--content-background, #ffffff);
16
+ color: var(--content-color, #1e293b);
17
+ font-size: 13px;
18
+ padding: 16px 20px;
19
+ gap: 14px;
20
+ box-sizing: border-box;
21
+ }
22
+ .header-card {
23
+ @apply --horizontal;
24
+ align-items: center;
25
+ justify-content: space-between;
26
+ gap: 12px;
27
+ padding: 12px 16px;
28
+ border-radius: 8px;
29
+ border: 1px solid var(--border-color, #cbd5e1);
30
+ background: var(--subtle-background, #f8fafc);
31
+ flex-wrap: wrap;
32
+ }
33
+ .header-left {
34
+ @apply --horizontal;
35
+ align-items: center;
36
+ gap: 12px;
37
+ min-width: 0;
38
+ }
39
+ .title {
40
+ font-size: 16px;
41
+ font-weight: 700;
42
+ }
43
+ .desc {
44
+ font-size: 12px;
45
+ opacity: 0.8;
46
+ margin-top: 2px;
47
+ }
48
+ .badge {
49
+ display: inline-flex;
50
+ align-items: center;
51
+ gap: 5px;
52
+ padding: 3px 10px;
53
+ border-radius: 999px;
54
+ font-size: 12px;
55
+ font-weight: 600;
56
+ white-space: nowrap;
57
+ }
58
+ .badge.active {
59
+ background: light-dark(#dcfce7, #14532d);
60
+ color: light-dark(#166534, #bbf7d0);
61
+ border: 1px solid light-dark(#86efac, #16a34a);
62
+ }
63
+ .badge.inactive {
64
+ background: light-dark(#f1f5f9, #27272a);
65
+ color: light-dark(#64748b, #a1a1aa);
66
+ border: 1px solid light-dark(#cbd5e1, #3f3f46);
67
+ }
68
+ .dot {
69
+ width: 8px;
70
+ height: 8px;
71
+ border-radius: 50%;
72
+ display: inline-block;
73
+ }
74
+ .dot.on { background: #22c55e; }
75
+ .dot.off { background: #94a3b8; }
76
+
77
+ .grid-2 {
78
+ display: grid;
79
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
80
+ gap: 14px;
81
+ }
82
+ .card {
83
+ @apply --vertical;
84
+ gap: 10px;
85
+ padding: 14px 16px;
86
+ border-radius: 8px;
87
+ border: 1px solid var(--border-color, #cbd5e1);
88
+ background: var(--content-background, #ffffff);
89
+ }
90
+ .section-title {
91
+ font-weight: 700;
92
+ font-size: 12px;
93
+ text-transform: uppercase;
94
+ letter-spacing: 0.04em;
95
+ opacity: 0.75;
96
+ border-bottom: 1px solid var(--border-color, #e2e8f0);
97
+ padding-bottom: 6px;
98
+ }
99
+ .field {
100
+ @apply --vertical;
101
+ gap: 4px;
102
+ }
103
+ .field label {
104
+ font-size: 12px;
105
+ font-weight: 600;
106
+ }
107
+ input:not([type="checkbox"]), select {
108
+ padding: 6px 10px;
109
+ font-size: 13px;
110
+ border: 1px solid var(--border-color, #cbd5e1);
111
+ border-radius: 6px;
112
+ background: var(--content-background, #ffffff);
113
+ color: inherit;
114
+ }
115
+ .toolbar {
116
+ @apply --horizontal;
117
+ gap: 8px;
118
+ align-items: center;
119
+ flex-wrap: wrap;
120
+ }
121
+ .btn {
122
+ display: inline-flex;
123
+ align-items: center;
124
+ gap: 6px;
125
+ padding: 6px 14px;
126
+ font-size: 12.5px;
127
+ font-weight: 600;
128
+ border-radius: 6px;
129
+ border: 1px solid var(--border-color, #cbd5e1);
130
+ background: var(--subtle-background, #f1f5f9);
131
+ color: inherit;
132
+ cursor: pointer;
133
+ }
134
+ .btn.primary {
135
+ background: var(--accent-color, #2563eb);
136
+ border-color: var(--accent-color, #2563eb);
137
+ color: #ffffff;
138
+ }
139
+ .model-row {
140
+ @apply --horizontal;
141
+ align-items: center;
142
+ justify-content: space-between;
143
+ gap: 10px;
144
+ padding: 8px 10px;
145
+ border-radius: 6px;
146
+ border: 1px solid var(--border-color, #e2e8f0);
147
+ background: var(--subtle-background, #f8fafc);
148
+ }
149
+ .model-row.off {
150
+ opacity: 0.55;
151
+ }
152
+ .pill {
153
+ font-size: 11px;
154
+ padding: 1px 6px;
155
+ border-radius: 4px;
156
+ border: 1px solid var(--border-color, #cbd5e1);
157
+ background: var(--content-background, #ffffff);
158
+ }
159
+ .msg {
160
+ padding: 8px 12px;
161
+ border-radius: 6px;
162
+ font-size: 12.5px;
163
+ }
164
+ .msg.ok {
165
+ background: light-dark(#dcfce7, #14532d);
166
+ color: light-dark(#166534, #bbf7d0);
167
+ }
168
+ .msg.err {
169
+ background: light-dark(#fee2e2, #450a0a);
170
+ color: light-dark(#991b1b, #fecaca);
171
+ }
172
+ </style>
173
+
174
+ <div class="header-card" ~if="state">
175
+ <div class="header-left">
176
+ <oda-icon :icon="state.icon || 'carbon:api'" icon-size="32"></oda-icon>
177
+ <div>
178
+ <div class="title">{{state.label}}</div>
179
+ <div class="desc" ~if="state.description">{{state.description}}</div>
180
+ </div>
181
+ </div>
182
+ <div class="toolbar">
183
+ <span class="badge" ~class="state.active ? 'active' : 'inactive'">
184
+ <span class="dot" ~class="state.active ? 'on' : 'off'"></span>
185
+ <span>{{state.active ? 'Активен' : ('Неактивен: ' + (state.statusReason || 'Не настроен ключ'))}}</span>
186
+ </span>
187
+ <label class="toolbar" style="cursor:pointer;font-weight:600;">
188
+ <input type="checkbox" :checked="state.enabled !== false" @change="toggleProviderEnabled($event.target.checked)">
189
+ <span>Провайдер включён</span>
190
+ </label>
191
+ </div>
192
+ </div>
193
+
194
+ <div class="msg ok" ~if="notice">{{notice}}</div>
195
+ <div class="msg err" ~if="error">{{error}}</div>
196
+
197
+ <div class="grid-2" ~if="state">
198
+ <!-- Левая карточка: Ключ доступа и параметры API -->
199
+ <div class="card">
200
+ <div class="section-title">1. Ключ доступа и подключение (#secret/{{state.secretFile}})</div>
201
+
202
+ <div class="field">
203
+ <label>{{state.keyLabel || 'API-ключ'}} <span ~if="state.hasKey" style="color:#16a34a;font-weight:700;">(сохранён: {{state.maskedKey}})</span></label>
204
+ <input type="password" :value="apiKeyInput" :placeholder="state.hasKey ? 'Оставьте пустым, чтобы не менять (' + state.maskedKey + ')' : state.keyPlaceholder" @input="apiKeyInput = $this.value">
205
+ </div>
206
+
207
+ <div class="field" ~if="state.needsFolderId">
208
+ <label>ID каталога Yandex Cloud (Folder ID)</label>
209
+ <input :value="folderIdInput" :placeholder="state.folderPlaceholder || 'b1g...'" @input="folderIdInput = $this.value">
210
+ </div>
211
+
212
+ <div class="field" ~if="state.protocol === 'gigachat'">
213
+ <label>Scope (версия API GigaChat)</label>
214
+ <select @change="scopeInput = $this.value">
215
+ <option value="GIGACHAT_API_PERS" :selected="scopeInput === 'GIGACHAT_API_PERS'">GIGACHAT_API_PERS (Физлица)</option>
216
+ <option value="GIGACHAT_API_B2B" :selected="scopeInput === 'GIGACHAT_API_B2B'">GIGACHAT_API_B2B (ИП / Юрлица по пакетам)</option>
217
+ <option value="GIGACHAT_API_CORP" :selected="scopeInput === 'GIGACHAT_API_CORP'">GIGACHAT_API_CORP (Корпоративный pay-as-you-go)</option>
218
+ </select>
219
+ </div>
220
+
221
+ <div class="field">
222
+ <label>Адрес API (Base URL)</label>
223
+ <input :value="baseUrlInput" @input="baseUrlInput = $this.value">
224
+ </div>
225
+
226
+ <div class="field">
227
+ <label>Протокол</label>
228
+ <select @change="protocolInput = $this.value">
229
+ <option value="openai" :selected="protocolInput === 'openai'">OpenAI-совместимый (openai)</option>
230
+ <option value="gigachat" :selected="protocolInput === 'gigachat'">Сбер GigaChat (gigachat)</option>
231
+ <option value="yandex" :selected="protocolInput === 'yandex'">Яндекс Cloud AI / Алиса (yandex)</option>
232
+ <option value="anthropic" :selected="protocolInput === 'anthropic'">Anthropic Messages API (anthropic)</option>
233
+ <option value="ollama" :selected="protocolInput === 'ollama'">Ollama / Локальный сервер (ollama)</option>
234
+ </select>
235
+ </div>
236
+
237
+ <div class="toolbar" style="margin-top:4px;">
238
+ <button type="button" class="btn primary" @tap="saveConfig()">Сохранить ключ и настройки</button>
239
+ <button type="button" class="btn" ~if="state.hasKey" @tap="clearKey()">Очистить ключ</button>
240
+ <button type="button" class="btn" @tap="fetchRemoteModels()">Проверить API / Загрузить модели</button>
241
+ </div>
242
+ </div>
243
+
244
+ <!-- Правая карточка: Модели провайдера -->
245
+ <div class="card">
246
+ <div class="section-title">2. Подключённые модели в дереве ({{state.installedModels?.length || 0}})</div>
247
+
248
+ <div ~if="!state.installedModels?.length" style="opacity:0.75;padding:6px 0;">
249
+ У провайдера пока нет добавленных моделей. Добавьте их из списка ниже.
250
+ </div>
251
+
252
+ <div class="model-row" ~for="state.installedModels" ~class="{'off': !$for.item.active}">
253
+ <div class="toolbar" style="min-width:0;">
254
+ <span class="dot" ~class="$for.item.active ? 'on' : 'off'"></span>
255
+ <div style="min-width:0;">
256
+ <div style="font-weight:700;">{{$for.item.label}}</div>
257
+ <div style="font-size:11.5px;opacity:0.75;">model: <code>{{$for.item.model}}</code> · ctx: {{$for.item.maxTokens}}</div>
258
+ </div>
259
+ </div>
260
+ <div class="toolbar">
261
+ <span class="pill" ~for="$for.item.capabilities">{{$for.$for.item}}</span>
262
+ <label class="toolbar" style="cursor:pointer;font-size:12px;">
263
+ <input type="checkbox" :checked="$for.item.enabled !== false" @change="toggleModel($for.item.path, $event.target.checked)">
264
+ <span>Вкл</span>
265
+ </label>
266
+ </div>
267
+ </div>
268
+
269
+ <div class="section-title" style="margin-top:8px;">3. Быстрое добавление модели</div>
270
+ <div class="toolbar">
271
+ <input style="flex:1;min-width:150px;" :value="customModelId" placeholder="ID модели на API (например: gpt-4.1, deepseek-chat)…" @input="customModelId = $this.value">
272
+ <input style="flex:1;min-width:140px;" :value="customModelLabel" placeholder="Название в дереве (опц.)" @input="customModelLabel = $this.value">
273
+ <button type="button" class="btn primary" @tap="addCustomModel()">+ Добавить</button>
274
+ </div>
275
+
276
+ <div class="toolbar" ~if="missingPresetModels.length" style="margin-top:4px;">
277
+ <span style="font-size:12px;font-weight:600;">Из каталога провайдера:</span>
278
+ <button type="button" class="btn" ~for="missingPresetModels" @tap="addPresetModel($for.item)">+ {{$for.item.label}}</button>
279
+ </div>
280
+
281
+ <div ~if="remoteModels.length" style="margin-top:6px;">
282
+ <div style="font-size:12px;font-weight:600;margin-bottom:4px;">Найдено на сервере API ({{remoteModels.length}}):</div>
283
+ <div class="toolbar">
284
+ <button type="button" class="btn" ~for="remoteModels" @tap="addRemoteModel($for.item)">+ {{$for.item}}</button>
285
+ </div>
286
+ </div>
287
+ </div>
288
+ </div>
289
+ `,
290
+ state: null,
291
+ apiKeyInput: '',
292
+ folderIdInput: '',
293
+ scopeInput: 'GIGACHAT_API_PERS',
294
+ baseUrlInput: '',
295
+ protocolInput: 'openai',
296
+ customModelId: '',
297
+ customModelLabel: '',
298
+ remoteModels: [],
299
+ notice: '',
300
+ error: '',
301
+ get missingPresetModels() {
302
+ const installed = new Set((this.state?.installedModels || []).map(m => m.model));
303
+ return (this.state?.presetModels || []).filter(m => !installed.has(m.model));
304
+ },
305
+ async attached() {
306
+ await this.reload();
307
+ },
308
+ async reload() {
309
+ if (!this.$item)
310
+ return;
311
+ try {
312
+ const st = await this.$item.fetch('provider_status');
313
+ this.state = st;
314
+ this.folderIdInput = st?.folderId || '';
315
+ this.scopeInput = st?.scope || 'GIGACHAT_API_PERS';
316
+ this.baseUrlInput = st?.baseUrl || '';
317
+ this.protocolInput = st?.protocol || 'openai';
318
+ }
319
+ catch (e) {
320
+ this.error = String(e?.message || e);
321
+ }
322
+ },
323
+ _refreshModelsTree() {
324
+ for (const w of [window, window.parent, window.top]) {
325
+ try {
326
+ const wk = w?.WORK;
327
+ if (!wk?.items)
328
+ continue;
329
+ for (const k of Object.keys(wk.items)) {
330
+ if (k === '/MODELS' || k.startsWith('/MODELS/')) {
331
+ const node = wk.items[k];
332
+ if (node?.[w.R]?.cache)
333
+ delete node[w.R].cache.items;
334
+ node?.reset?.();
335
+ }
336
+ }
337
+ }
338
+ catch { /* ignore */ }
339
+ }
340
+ },
341
+ async saveConfig() {
342
+ this.error = '';
343
+ this.notice = '';
344
+ if (!this.$item)
345
+ return;
346
+ try {
347
+ const payload = {
348
+ baseUrl: this.baseUrlInput,
349
+ protocol: this.protocolInput,
350
+ scope: this.scopeInput,
351
+ folderId: this.folderIdInput,
352
+ };
353
+ if (this.apiKeyInput.trim())
354
+ payload.apiKey = this.apiKeyInput.trim();
355
+ const res = await this.$item.fetch('provider_save_config', payload);
356
+ this.apiKeyInput = '';
357
+ this.state = res?.status || await this.$item.fetch('provider_status');
358
+ this.notice = 'Настройки и ключ провайдера сохранены.';
359
+ this._refreshModelsTree();
360
+ }
361
+ catch (e) {
362
+ this.error = String(e?.message || e);
363
+ }
364
+ },
365
+ async clearKey() {
366
+ this.error = '';
367
+ this.notice = '';
368
+ if (!this.$item)
369
+ return;
370
+ try {
371
+ const res = await this.$item.fetch('provider_save_config', { apiKey: '' });
372
+ this.apiKeyInput = '';
373
+ this.state = res?.status || await this.$item.fetch('provider_status');
374
+ this.notice = 'Ключ удалён — провайдер переведён в неактивное состояние.';
375
+ this._refreshModelsTree();
376
+ }
377
+ catch (e) {
378
+ this.error = String(e?.message || e);
379
+ }
380
+ },
381
+ async toggleProviderEnabled(checked) {
382
+ this.error = '';
383
+ if (!this.$item)
384
+ return;
385
+ try {
386
+ await this.$item.fetch('provider_toggle_model', {
387
+ nodePath: this.$item.path,
388
+ enabled: checked,
389
+ });
390
+ await this.reload();
391
+ this._refreshModelsTree();
392
+ }
393
+ catch (e) {
394
+ this.error = String(e?.message || e);
395
+ }
396
+ },
397
+ async toggleModel(modelPath, checked) {
398
+ this.error = '';
399
+ if (!this.$item)
400
+ return;
401
+ try {
402
+ await this.$item.fetch('provider_toggle_model', {
403
+ nodePath: modelPath,
404
+ enabled: checked,
405
+ });
406
+ await this.reload();
407
+ this._refreshModelsTree();
408
+ }
409
+ catch (e) {
410
+ this.error = String(e?.message || e);
411
+ }
412
+ },
413
+ async addCustomModel() {
414
+ const model = this.customModelId.trim();
415
+ if (!model)
416
+ return;
417
+ const label = this.customModelLabel.trim() || model;
418
+ this.error = '';
419
+ try {
420
+ const res = await this.$item.fetch('provider_add_models', {
421
+ models: [{ id: label, label, model }],
422
+ });
423
+ this.customModelId = '';
424
+ this.customModelLabel = '';
425
+ this.state = res?.status || await this.$item.fetch('provider_status');
426
+ this.notice = `Модель «${label}» добавлена в дерево провайдера.`;
427
+ this._refreshModelsTree();
428
+ }
429
+ catch (e) {
430
+ this.error = String(e?.message || e);
431
+ }
432
+ },
433
+ async addPresetModel(spec) {
434
+ this.error = '';
435
+ try {
436
+ const res = await this.$item.fetch('provider_add_models', {
437
+ models: [spec],
438
+ });
439
+ this.state = res?.status || await this.$item.fetch('provider_status');
440
+ this.notice = `Модель «${spec.label}» добавлена.`;
441
+ this._refreshModelsTree();
442
+ }
443
+ catch (e) {
444
+ this.error = String(e?.message || e);
445
+ }
446
+ },
447
+ async addRemoteModel(modelId) {
448
+ this.error = '';
449
+ try {
450
+ const res = await this.$item.fetch('provider_add_models', {
451
+ models: [{ id: modelId, label: modelId, model: modelId }],
452
+ });
453
+ this.state = res?.status || await this.$item.fetch('provider_status');
454
+ this.notice = `Модель «${modelId}» добавлена.`;
455
+ this._refreshModelsTree();
456
+ }
457
+ catch (e) {
458
+ this.error = String(e?.message || e);
459
+ }
460
+ },
461
+ async fetchRemoteModels() {
462
+ this.error = '';
463
+ this.notice = '';
464
+ if (!this.$item)
465
+ return;
466
+ try {
467
+ const res = await this.$item.fetch('list_remote');
468
+ if (res?.error) {
469
+ this.error = res.error + (res.tried?.length ? ' (' + res.tried.join('; ') + ')' : '');
470
+ return;
471
+ }
472
+ this.remoteModels = Array.isArray(res?.models) ? res.models : [];
473
+ this.notice = `Подключение успешно (${res.source}): доступно моделей — ${this.remoteModels.length}.`;
474
+ }
475
+ catch (e) {
476
+ this.error = String(e?.message || e);
477
+ }
478
+ },
479
+ }
@@ -437,7 +437,7 @@ export default {
437
437
  <button type="button" class="tab-btn" ~class="{'tab-active': activeTab === 'hf'}" @tap="activeTab = 'hf'">1. Каталог моделей Hugging Face ({{hfModels.length}})</button>
438
438
  <button type="button" class="tab-btn" ~class="{'tab-active': activeTab === 'dataset'}" @tap="activeTab = 'dataset'">2. Датасеты WORK ({{datasets.length}})</button>
439
439
  <button type="button" class="tab-btn" ~class="{'tab-active': activeTab === 'train'}" @tap="activeTab = 'train'">3. Дообучение (LoRA / SFT / GRPO)</button>
440
- <button type="button" class="tab-btn" ~class="{'tab-active': activeTab === 'halogen'}" @tap="openHalogenTab()">4. Контур Halogen (Ryzen AI 192 ГБ: GPU + NPU)</button>
440
+ <button type="button" class="tab-btn" ~class="{'tab-active': activeTab === 'halogen'}" @tap="openHalogenTab()">4. Машинки Halo (Ryzen AI 128–192 ГБ: GPU + NPU)</button>
441
441
  </div>
442
442
 
443
443
  <div class="error" ~if="error">{{error}}</div>
@@ -752,7 +752,7 @@ export default {
752
752
  </div>
753
753
 
754
754
  <div class="cards-scroll">
755
- <div class="section-title">Матрица моделей /MODELS/Halogen ({{halogenCatalog.length}})</div>
755
+ <div class="section-title">Матрица моделей Halo для установки в /MODELS/Local ({{halogenCatalog.length}})</div>
756
756
  <div class="card" ~for="halogenCatalog">
757
757
  <div class="model-top">
758
758
  <div class="model-title-group">
@@ -953,7 +953,7 @@ export default {
953
953
  const wk = w?.WORK;
954
954
  if (!wk?.items)
955
955
  continue;
956
- for (const p of ['/MODELS', '/MODELS/Local', '/MODELS/Halogen']) {
956
+ for (const p of ['/MODELS', '/MODELS/Local']) {
957
957
  const node = wk.items[p];
958
958
  if (node?.[w.R]?.cache)
959
959
  delete node[w.R].cache.items;
@@ -1,20 +1,10 @@
1
- /**
2
- * GigaChat — прототип для моделей провайдера GigaChat.
3
- *
4
- * Наследуется всеми моделями внутри models/GigaChat/.
5
- * Содержит общие настройки подключения и метаданные GigaChat.
6
- */
7
- export default {
8
- icon: 'ai:gigachat',
9
- description: 'Провайдер GigaChat',
10
-
11
- protocol: 'gigachat',
12
- baseUrl: 'https://gigachat.devices.sberbank.ru/api/v1/chat/completions',
13
- authUrl: 'https://ngw.devices.sberbank.ru:9443/api/v2/oauth',
14
- /** Authorization key: файл /MODELS/GigaChat/$provider/#secret/gigachat.json ({"token": "..."}); запасной вариант — env GIGACHAT_KEY. */
15
- token: 'secret:gigachat.json',
16
- scope: 'GIGACHAT_API_PERS',
17
-
18
- /** Native tools / ask_user через streamChat (OpenAI-compatible functions) */
19
- functionCalling: true,
20
- }
1
+ export default {
2
+ "icon": "ai:gigachat",
3
+ "description": "Модель провайдера GigaChat (Сбер)",
4
+ "protocol": "gigachat",
5
+ "baseUrl": "https://gigachat.devices.sberbank.ru/api/v1/chat/completions",
6
+ "functionCalling": true,
7
+ "authUrl": "https://ngw.devices.sberbank.ru:9443/api/v2/oauth",
8
+ "token": "secret:gigachat.json",
9
+ "scope": "GIGACHAT_API_PERS"
10
+ };
@@ -1,7 +1,10 @@
1
- /**
2
- * GigaChat — провайдер ($provider) моделей GigaChat.
3
- */
4
- export default {
5
- icon: 'ai:gigachat',
6
- label: 'GigaChat',
7
- }
1
+ export default {
2
+ "icon": "ai:gigachat",
3
+ "label": "GigaChat (Сбер)",
4
+ "form": "provider",
5
+ "protocol": "gigachat",
6
+ "baseUrl": "https://gigachat.devices.sberbank.ru/api/v1/chat/completions",
7
+ "authUrl": "https://ngw.devices.sberbank.ru:9443/api/v2/oauth",
8
+ "token": "secret:gigachat.json",
9
+ "scope": "GIGACHAT_API_PERS"
10
+ };
@@ -0,0 +1,15 @@
1
+ export default {
2
+ "label": "GigaChat 2 Lite",
3
+ "model": "GigaChat-2",
4
+ "icon": "ai:gigachat",
5
+ "modality": "llm",
6
+ "maxTokens": 131072,
7
+ "maxOutput": 8192,
8
+ "functionCalling": true,
9
+ "capabilities": [
10
+ "chat",
11
+ "stream",
12
+ "tools",
13
+ "functions"
14
+ ]
15
+ };
@@ -0,0 +1,16 @@
1
+ export default {
2
+ "label": "GigaChat 2 Max",
3
+ "model": "GigaChat-2-Max",
4
+ "icon": "ai:gigachat",
5
+ "modality": "vlm",
6
+ "maxTokens": 131072,
7
+ "maxOutput": 8192,
8
+ "functionCalling": true,
9
+ "capabilities": [
10
+ "chat",
11
+ "stream",
12
+ "tools",
13
+ "functions",
14
+ "vision"
15
+ ]
16
+ };
@@ -0,0 +1,15 @@
1
+ export default {
2
+ "label": "GigaChat 2 Pro",
3
+ "model": "GigaChat-2-Pro",
4
+ "icon": "ai:gigachat",
5
+ "modality": "llm",
6
+ "maxTokens": 131072,
7
+ "maxOutput": 8192,
8
+ "functionCalling": true,
9
+ "capabilities": [
10
+ "chat",
11
+ "stream",
12
+ "tools",
13
+ "functions"
14
+ ]
15
+ };
@@ -0,0 +1,13 @@
1
+ export default {
2
+ "label": "GigaChat Embeddings",
3
+ "model": "EmbeddingsGigaR",
4
+ "icon": "carbon:search-locate",
5
+ "modality": "embed",
6
+ "maxTokens": 4096,
7
+ "maxOutput": 1024,
8
+ "functionCalling": false,
9
+ "capabilities": [
10
+ "embeddings",
11
+ "rag"
12
+ ]
13
+ };
@@ -1,11 +1,11 @@
1
1
  /**
2
- * Local — прототип моделей ($ai) локального провайдера /MODELS/Local.
2
+ * Local — прототип моделей ($ai) единого локального провайдера /MODELS/Local.
3
3
  * Наследуется всеми локальными моделями через tilde (cross-type $provider → $ai).
4
4
  */
5
5
  export default {
6
6
  icon: 'ai:ollama',
7
7
  description: 'Локальная модель ИИ',
8
- protocol: 'openai',
9
- baseUrl: 'http://127.0.0.1:8080/v1/chat/completions',
8
+ protocol: 'local',
9
+ baseUrl: 'http://127.0.0.1:8731/v1/chat/completions',
10
10
  functionCalling: true,
11
11
  }
@@ -1,10 +1,14 @@
1
1
  /**
2
- * Local — провайдер ($provider) локальных моделей и встроенного дообучения ИИ.
2
+ * Local — единый провайдер ($provider) локальных моделей ИИ, дообучения (LoRA / SFT / GRPO)
3
+ * и аппаратного контура Halo (AMD Ryzen™ AI Max+ PRO 395/495 · 128–192 ГБ Unified Memory: GPU + NPU).
4
+ *
5
+ * В чистой поставке папка /MODELS/Local пуста — дочерние узлы моделей ($ai) создаются автоматически
6
+ * только после реального скачивания весов модели в /MODELS/Local/<Модель>/#weights.
3
7
  */
4
8
  export default {
5
- icon: 'carbon:location-company',
9
+ icon: 'carbon:chip',
6
10
  label: 'Local',
7
11
  protocol: 'local',
8
12
  form: 'trainer',
9
- baseUrl: 'http://127.0.0.1:8080/v1/chat/completions',
13
+ baseUrl: 'http://127.0.0.1:8731/v1/chat/completions',
10
14
  }
@@ -0,0 +1,8 @@
1
+ export default {
2
+ "icon": "ai:mts",
3
+ "description": "Модель провайдера MTS AI (Cotype)",
4
+ "protocol": "openai",
5
+ "baseUrl": "https://api.mts.ai/v1/chat/completions",
6
+ "functionCalling": true,
7
+ "apiKey": "secret:mts.json"
8
+ };