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.
- package/#system/package-manifest.json +57 -49
- package/$server/$folder/handlers/methods/create/$handler/class.js +83 -2
- package/$server/$folder/handlers/pages/form/$handler/form.js +2 -1
- 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 +58 -3
- package/MODELS/$ai/$folder/$class/$ai/handlers/pages/form/trainer/$handler/class.js +6 -4
- package/MODELS/$ai/$folder/$class/$ai/handlers/pages/form/trainer/$handler/trainer.js +344 -3
- 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/class.js +3 -3
- package/MODELS/$ai/$folder/$class/$provider/handlers/pages/form/trainer/$handler/trainer.js +530 -920
- 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 +24 -0
- 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 +176 -3
- package/sources/modules/trainer/local-engine.js +67 -1
- package/sources/modules/trainer/providers.js +1213 -0
- package/sources/modules/trainer/runner.js +47 -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
|
@@ -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
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/** Мета хендлера form/trainer (
|
|
1
|
+
/** Мета хендлера form/trainer (Модели ИИ для локального провайдера /MODELS/Local). */
|
|
2
2
|
export default {
|
|
3
|
-
icon: 'carbon:
|
|
4
|
-
label: '
|
|
3
|
+
icon: 'carbon:chip',
|
|
4
|
+
label: 'Модели ИИ',
|
|
5
5
|
roles: ['BOSS'],
|
|
6
6
|
order: 1,
|
|
7
7
|
allowUse($context) {
|