odant-work 1.0.15 → 1.0.17

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 (84) hide show
  1. package/#system/package-manifest.json +57 -49
  2. package/$server/$folder/handlers/methods/create/$handler/class.js +83 -2
  3. package/$server/$folder/handlers/pages/form/$handler/form.js +2 -1
  4. package/$server/$folder/lib/icon/icon.js +14 -0
  5. package/$server/$folder/lib/node/node.js +51 -1
  6. package/$server/$folder/lib/tree/tree.js +5 -1
  7. package/.secretsignore +4 -0
  8. package/MODELS/$ai/$folder/$class/$ai/class.js +58 -3
  9. package/MODELS/$ai/$folder/$class/$ai/handlers/pages/form/trainer/$handler/class.js +6 -4
  10. package/MODELS/$ai/$folder/$class/$ai/handlers/pages/form/trainer/$handler/trainer.js +344 -3
  11. package/MODELS/$ai/$folder/$class/$provider/class.js +1 -1
  12. package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/provider/$handler/class.js +13 -0
  13. package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/provider/$handler/provider.js +479 -0
  14. package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/trainer/$handler/class.js +3 -3
  15. package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/trainer/$handler/trainer.js +530 -920
  16. package/MODELS/GigaChat/$provider/$folder/$class/$ai/class.js +10 -20
  17. package/MODELS/GigaChat/$provider/class.js +10 -7
  18. package/MODELS/GigaChat/GigaChat 2 Lite/$ai/class.js +15 -0
  19. package/MODELS/GigaChat/GigaChat 2 Max/$ai/class.js +16 -0
  20. package/MODELS/GigaChat/GigaChat 2 Pro/$ai/class.js +15 -0
  21. package/MODELS/GigaChat/GigaChat Embeddings/$ai/class.js +13 -0
  22. package/MODELS/Local/$provider/$folder/$class/$ai/class.js +3 -3
  23. package/MODELS/Local/$provider/class.js +7 -3
  24. package/MODELS/MTS-AI/$provider/$folder/$class/$ai/class.js +8 -0
  25. package/MODELS/MTS-AI/$provider/class.js +8 -0
  26. package/MODELS/MTS-AI/Cotype Lite/$ai/class.js +15 -0
  27. package/MODELS/MTS-AI/Cotype Pro 2/$ai/class.js +15 -0
  28. package/MODELS/MTS-AI/Cotype Vision/$ai/class.js +14 -0
  29. package/MODELS/T-Bank/$provider/$folder/$class/$ai/class.js +8 -0
  30. package/MODELS/T-Bank/$provider/class.js +8 -0
  31. package/MODELS/T-Bank/T-Lite 2.0/$ai/class.js +15 -0
  32. package/MODELS/T-Bank/T-Pro 2.0/$ai/class.js +16 -0
  33. package/MODELS/T-Bank/T-VoiceKit ASR/$ai/class.js +13 -0
  34. package/MODELS/T-Bank/T-VoiceKit TTS/$ai/class.js +13 -0
  35. package/MODELS/Yandex/$provider/$folder/$class/$ai/class.js +9 -0
  36. package/MODELS/Yandex/$provider/class.js +9 -0
  37. package/MODELS/Yandex/Alice AI (YandexGPT 5 Pro)/$ai/class.js +15 -0
  38. package/MODELS/Yandex/Yandex Embeddings/$ai/class.js +13 -0
  39. package/MODELS/Yandex/Yandex SpeechKit ASR/$ai/class.js +13 -0
  40. package/MODELS/Yandex/Yandex SpeechKit TTS/$ai/class.js +13 -0
  41. package/MODELS/Yandex/YandexART/$ai/class.js +13 -0
  42. package/MODELS/Yandex/YandexGPT 5 Lite/$ai/class.js +15 -0
  43. package/MODELS/Yandex/YandexGPT Reasoning/$ai/class.js +22 -0
  44. package/oda/tools/icons/lib/svg/ai.svg +20 -0
  45. package/package.json +1 -1
  46. package/scripts/check-ignored-code.mjs +3 -0
  47. package/scripts/check-secrets.mjs +1 -1
  48. package/sources/data/progress.md +24 -0
  49. package/sources/host/watch.js +13 -2
  50. package/sources/modules/agent/index.js +23 -2
  51. package/sources/modules/trainer/halogen.js +128 -29
  52. package/sources/modules/trainer/huggingface.js +11 -5
  53. package/sources/modules/trainer/index.js +176 -3
  54. package/sources/modules/trainer/local-engine.js +67 -1
  55. package/sources/modules/trainer/providers.js +1213 -0
  56. package/sources/modules/trainer/runner.js +47 -1
  57. package/sources/server/class.js +55 -3
  58. package/MODELS/GigaChat/GigaChat Light/$ai/class.js +0 -15
  59. package/MODELS/GigaChat/GigaChat Pro/$ai/class.js +0 -15
  60. package/MODELS/Halogen/$provider/$folder/$class/$ai/class.js +0 -11
  61. package/MODELS/Halogen/$provider/class.js +0 -16
  62. package/MODELS/Halogen/$provider/readme.md +0 -28
  63. package/MODELS/Halogen/Decider-NPU/$ai/class.js +0 -22
  64. package/MODELS/Halogen/Flux2-Klein-NPU/$ai/class.js +0 -21
  65. package/MODELS/Halogen/Qwen3-ASR-NPU/$ai/class.js +0 -21
  66. package/MODELS/Halogen/Qwen3-Embedding-NPU/$ai/class.js +0 -22
  67. package/MODELS/Halogen/Qwen3-Reranker-NPU/$ai/class.js +0 -21
  68. package/MODELS/Halogen/Qwen3-TTS-NPU/$ai/class.js +0 -26
  69. package/MODELS/Halogen/Qwen3.5-2B-NPU/$ai/class.js +0 -23
  70. package/MODELS/Halogen/Qwen3.8-Flash-Next/$ai/class.js +0 -31
  71. package/MODELS/Local/Flux-Image/$ai/class.js +0 -20
  72. package/MODELS/Local/Qwen3-0.6B/$ai/class.js +0 -26
  73. package/MODELS/Local/Qwen3-14B/$ai/class.js +0 -27
  74. package/MODELS/Local/Qwen3-30B-A3B/$ai/class.js +0 -30
  75. package/MODELS/Local/Qwen3-Embedding/$ai/class.js +0 -21
  76. package/MODELS/Local/Qwen3-Voice/$ai/class.js +0 -26
  77. package/MODELS/odant/$provider/$folder/$class/$ai/class.js +0 -11
  78. package/MODELS/odant/$provider/class.js +0 -75
  79. package/MODELS/odant/$provider/readme.md +0 -21
  80. package/MODELS/odant/Qwen3-ASR/$ai/class.js +0 -15
  81. package/MODELS/odant/Qwen3-TTS/$ai/class.js +0 -22
  82. package/MODELS/odant/Qwen3.8 27b/$ai/class.js +0 -11
  83. package/MODELS/odant/Z-Image-Turbo/$ai/class.js +0 -8
  84. package/MODELS/odant/Z-Image-Turbo/$ai/readme.md +0 -24
@@ -1,6 +1,347 @@
1
1
  /**
2
- * form/trainer для локальной модели $ai — наследует представление провайдера.
2
+ * form/trainer для конкретного узла модели $ai — чистая карточка параметров, состояния и проверки модели.
3
3
  */
4
- import trainerProto from '../../../../../$provider/handlers/pages/form/trainer/$handler/trainer.js';
4
+ export default {
5
+ imports: 'oda//icon',
6
+ template: /*html*/`
7
+ <style>
8
+ :host {
9
+ @apply --vertical;
10
+ @apply --flex;
11
+ overflow-y: auto;
12
+ background: var(--content-background, #ffffff);
13
+ color: var(--content-color, #1e293b);
14
+ font-size: 13px;
15
+ padding: 16px 20px;
16
+ gap: 14px;
17
+ box-sizing: border-box;
18
+ }
19
+ .header-card {
20
+ @apply --horizontal;
21
+ align-items: center;
22
+ justify-content: space-between;
23
+ gap: 12px;
24
+ padding: 12px 16px;
25
+ border-radius: 8px;
26
+ border: 1px solid var(--border-color, #cbd5e1);
27
+ background: var(--subtle-background, #f8fafc);
28
+ flex-wrap: wrap;
29
+ }
30
+ .header-left {
31
+ @apply --horizontal;
32
+ align-items: center;
33
+ gap: 12px;
34
+ min-width: 0;
35
+ }
36
+ .title {
37
+ font-size: 16px;
38
+ font-weight: 700;
39
+ }
40
+ .subtitle {
41
+ font-size: 12px;
42
+ opacity: 0.75;
43
+ margin-top: 2px;
44
+ }
45
+ .toolbar {
46
+ @apply --horizontal;
47
+ gap: 8px;
48
+ align-items: center;
49
+ flex-wrap: wrap;
50
+ }
51
+ .badge {
52
+ display: inline-flex;
53
+ align-items: center;
54
+ gap: 6px;
55
+ padding: 3px 10px;
56
+ border-radius: 999px;
57
+ font-size: 12px;
58
+ font-weight: 600;
59
+ }
60
+ .badge.active {
61
+ background: light-dark(#dcfce7, #14532d);
62
+ color: light-dark(#166534, #bbf7d0);
63
+ border: 1px solid light-dark(#86efac, #16a34a);
64
+ }
65
+ .badge.inactive {
66
+ background: light-dark(#f1f5f9, #27272a);
67
+ color: light-dark(#64748b, #a1a1aa);
68
+ border: 1px solid light-dark(#cbd5e1, #3f3f46);
69
+ }
70
+ .dot {
71
+ width: 8px;
72
+ height: 8px;
73
+ border-radius: 50%;
74
+ display: inline-block;
75
+ }
76
+ .dot.on { background: #22c55e; }
77
+ .dot.off { background: #94a3b8; }
78
+ .grid-2 {
79
+ display: grid;
80
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
81
+ gap: 14px;
82
+ }
83
+ .card {
84
+ @apply --vertical;
85
+ gap: 10px;
86
+ padding: 14px 16px;
87
+ border-radius: 8px;
88
+ border: 1px solid var(--border-color, #cbd5e1);
89
+ background: var(--content-background, #ffffff);
90
+ }
91
+ .section-title {
92
+ font-weight: 700;
93
+ font-size: 12px;
94
+ text-transform: uppercase;
95
+ letter-spacing: 0.04em;
96
+ opacity: 0.75;
97
+ border-bottom: 1px solid var(--border-color, #e2e8f0);
98
+ padding-bottom: 6px;
99
+ }
100
+ .kv-row {
101
+ @apply --horizontal;
102
+ justify-content: space-between;
103
+ align-items: center;
104
+ gap: 10px;
105
+ padding: 5px 0;
106
+ border-bottom: 1px solid var(--border-color, #f1f5f9);
107
+ font-size: 12.5px;
108
+ }
109
+ .kv-label {
110
+ opacity: 0.75;
111
+ }
112
+ .kv-val {
113
+ font-weight: 600;
114
+ text-align: right;
115
+ }
116
+ .pill {
117
+ font-size: 11px;
118
+ padding: 2px 7px;
119
+ border-radius: 4px;
120
+ border: 1px solid var(--border-color, #cbd5e1);
121
+ background: var(--subtle-background, #f8fafc);
122
+ }
123
+ .btn {
124
+ display: inline-flex;
125
+ align-items: center;
126
+ gap: 6px;
127
+ padding: 6px 14px;
128
+ font-size: 12.5px;
129
+ font-weight: 600;
130
+ border-radius: 6px;
131
+ border: 1px solid var(--border-color, #cbd5e1);
132
+ background: var(--subtle-background, #f1f5f9);
133
+ color: inherit;
134
+ cursor: pointer;
135
+ }
136
+ .btn.primary {
137
+ background: var(--accent-color, #2563eb);
138
+ border-color: var(--accent-color, #2563eb);
139
+ color: #ffffff;
140
+ }
141
+ .btn.danger {
142
+ color: light-dark(#b91c1c, #fca5a5);
143
+ border-color: light-dark(#fecaca, #7f1d1d);
144
+ }
145
+ textarea {
146
+ padding: 8px 10px;
147
+ font-size: 13px;
148
+ font-family: inherit;
149
+ border: 1px solid var(--border-color, #cbd5e1);
150
+ border-radius: 6px;
151
+ background: var(--content-background, #ffffff);
152
+ color: inherit;
153
+ resize: vertical;
154
+ }
155
+ .reply-box {
156
+ padding: 10px 12px;
157
+ border-radius: 6px;
158
+ border: 1px solid var(--border-color, #cbd5e1);
159
+ background: var(--subtle-background, #f8fafc);
160
+ white-space: pre-wrap;
161
+ line-height: 1.45;
162
+ font-size: 13px;
163
+ }
164
+ .msg {
165
+ padding: 8px 12px;
166
+ border-radius: 6px;
167
+ font-size: 12.5px;
168
+ }
169
+ .msg.ok {
170
+ background: light-dark(#dcfce7, #14532d);
171
+ color: light-dark(#166534, #bbf7d0);
172
+ }
173
+ .msg.err {
174
+ background: light-dark(#fee2e2, #450a0a);
175
+ color: light-dark(#991b1b, #fecaca);
176
+ }
177
+ </style>
5
178
 
6
- export default trainerProto;
179
+ <div class="header-card" ~if="info">
180
+ <div class="header-left">
181
+ <oda-icon :icon="info.icon || 'carbon:machine-learning-model'" icon-size="30"></oda-icon>
182
+ <div>
183
+ <div class="title">{{info.label || info.id}}</div>
184
+ <div class="subtitle">Провайдер: {{providerName}} · Путь: {{info.path}}</div>
185
+ </div>
186
+ </div>
187
+ <div class="toolbar">
188
+ <span class="badge" ~class="info.aiActive ? 'active' : 'inactive'">
189
+ <span class="dot" ~class="info.aiActive ? 'on' : 'off'"></span>
190
+ <span>{{info.aiActive ? 'Активна' : ('Неактивна: ' + (info.aiStatusReason || 'не настроена'))}}</span>
191
+ </span>
192
+ <label class="toolbar" style="cursor:pointer;font-weight:600;">
193
+ <input type="checkbox" :checked="modelEnabled" @change="toggleEnabled($event.target.checked)">
194
+ <span>Включена</span>
195
+ </label>
196
+ <button type="button" class="btn danger" ~if="isLocalModel" @tap="deleteModel()">Удалить с диска</button>
197
+ </div>
198
+ </div>
199
+
200
+ <div class="msg ok" ~if="notice">{{notice}}</div>
201
+ <div class="msg err" ~if="error">{{error}}</div>
202
+
203
+ <div class="grid-2" ~if="info">
204
+ <div class="card">
205
+ <div class="section-title">Параметры модели</div>
206
+ <div class="kv-row">
207
+ <span class="kv-label">Идентификатор модели (API):</span>
208
+ <span class="kv-val"><code>{{info.DATA?.model || info.id}}</code></span>
209
+ </div>
210
+ <div class="kv-row">
211
+ <span class="kv-label">Протокол:</span>
212
+ <span class="kv-val">{{info.DATA?.protocol || 'openai'}}</span>
213
+ </div>
214
+ <div class="kv-row">
215
+ <span class="kv-label">Размер контекста (maxTokens):</span>
216
+ <span class="kv-val">{{info.maxTokens || info.DATA?.maxTokens || 32768}} токенов</span>
217
+ </div>
218
+ <div class="kv-row">
219
+ <span class="kv-label">Макс. длина ответа (maxOutput):</span>
220
+ <span class="kv-val">{{info.maxOutput || info.DATA?.maxOutput || 8192}} токенов</span>
221
+ </div>
222
+ <div class="kv-row" ~if="info.DATA?.baseUrl">
223
+ <span class="kv-label">Адрес API (Base URL):</span>
224
+ <span class="kv-val" style="word-break:break-all;"><code>{{info.DATA.baseUrl}}</code></span>
225
+ </div>
226
+ <div class="kv-row">
227
+ <span class="kv-label">Возможности:</span>
228
+ <div class="toolbar" style="justify-content:flex-end;">
229
+ <span class="pill" ~for="capabilities">{{$for.item}}</span>
230
+ </div>
231
+ </div>
232
+ </div>
233
+
234
+ <div class="card">
235
+ <div class="section-title">Быстрая проверка ответа</div>
236
+ <textarea rows="3" :value="testPrompt" placeholder="Введите тестовый вопрос к модели…" @input="testPrompt = $this.value"></textarea>
237
+ <div class="toolbar">
238
+ <button type="button" class="btn primary" :disabled="testing" @tap="runTestPrompt()">
239
+ {{testing ? 'Ожидание ответа…' : 'Отправить запрос'}}
240
+ </button>
241
+ </div>
242
+ <div class="reply-box" ~if="testReply">{{testReply}}</div>
243
+ </div>
244
+ </div>
245
+ `,
246
+ info: null,
247
+ modelEnabled: true,
248
+ testPrompt: 'Привет! Представься коротко в одном предложении.',
249
+ testReply: '',
250
+ testing: false,
251
+ notice: '',
252
+ error: '',
253
+ get providerName() {
254
+ const parts = String(this.info?.path || this.$item?.path || '').split('/').filter(Boolean);
255
+ return parts[1] || 'MODELS';
256
+ },
257
+ get isLocalModel() {
258
+ return String(this.info?.path || this.$item?.path || '').startsWith('/MODELS/Local/');
259
+ },
260
+ get capabilities() {
261
+ const c = this.info?.capabilities || this.info?.DATA?.capabilities || ['chat', 'stream'];
262
+ return Array.isArray(c) ? c : String(c).split(/[\s,]+/).filter(Boolean);
263
+ },
264
+ async attached() {
265
+ await this.reload();
266
+ },
267
+ async reload() {
268
+ if (!this.$item)
269
+ return;
270
+ try {
271
+ const inf = await this.$item.fetch('info');
272
+ this.info = inf;
273
+ this.modelEnabled = inf?.DATA?.enabled !== false;
274
+ }
275
+ catch (e) {
276
+ this.error = String(e?.message || e);
277
+ }
278
+ },
279
+ _refreshTree() {
280
+ for (const w of [window, window.parent, window.top]) {
281
+ try {
282
+ const wk = w?.WORK;
283
+ if (!wk?.items)
284
+ continue;
285
+ for (const k of Object.keys(wk.items)) {
286
+ if (k === '/MODELS' || k.startsWith('/MODELS/')) {
287
+ const node = wk.items[k];
288
+ if (node?.[w.R]?.cache)
289
+ delete node[w.R].cache.items;
290
+ node?.reset?.();
291
+ }
292
+ }
293
+ }
294
+ catch { /* ignore */ }
295
+ }
296
+ },
297
+ async toggleEnabled(checked) {
298
+ this.error = '';
299
+ this.notice = '';
300
+ if (!this.$item)
301
+ return;
302
+ try {
303
+ await this.$item.fetch('provider_toggle_model', {
304
+ nodePath: this.$item.path,
305
+ enabled: checked,
306
+ });
307
+ await this.reload();
308
+ this._refreshTree();
309
+ this.notice = checked ? 'Модель включена.' : 'Модель отключена.';
310
+ }
311
+ catch (e) {
312
+ this.error = String(e?.message || e);
313
+ }
314
+ },
315
+ async deleteModel() {
316
+ if (!this.$item || !this.isLocalModel)
317
+ return;
318
+ this.error = '';
319
+ try {
320
+ await this.$item.fetch('trainer_delete_model', { modelId: this.$item.id });
321
+ this._refreshTree();
322
+ this.notice = 'Модель и её веса удалены с диска.';
323
+ }
324
+ catch (e) {
325
+ this.error = String(e?.message || e);
326
+ }
327
+ },
328
+ async runTestPrompt() {
329
+ if (!this.$item || !this.testPrompt.trim())
330
+ return;
331
+ this.testing = true;
332
+ this.error = '';
333
+ this.testReply = '';
334
+ try {
335
+ const res = await this.$item.fetch('chat', {
336
+ messages: [{ role: 'user', content: this.testPrompt.trim() }],
337
+ });
338
+ this.testReply = String(res?.content ?? res?.text ?? JSON.stringify(res));
339
+ }
340
+ catch (e) {
341
+ this.error = String(e?.message || e);
342
+ }
343
+ finally {
344
+ this.testing = false;
345
+ }
346
+ },
347
+ }
@@ -7,7 +7,7 @@
7
7
  export default {
8
8
  icon: 'carbon:api',
9
9
  label: 'Провайдер ИИ',
10
- form: 'editor',
10
+ form: 'provider',
11
11
  METADATA: {
12
12
  STATIC: [{
13
13
  id: 'protocol',
@@ -0,0 +1,13 @@
1
+ /** Мета хендлера form/provider (Модели ИИ и подключение облачного провайдера). */
2
+ export default {
3
+ icon: 'carbon:chip',
4
+ label: 'Модели ИИ',
5
+ roles: ['BOSS'],
6
+ order: 1,
7
+ allowUse($context) {
8
+ const p = String($context?.path || $context?.short || '');
9
+ if (p === '/MODELS/Local' || p.startsWith('/MODELS/Local/') || $context?.protocol === 'local' || $context?.protocol === 'halogen')
10
+ return false;
11
+ return true;
12
+ },
13
+ }