@skaile/forge-settings 1.3.3 → 2.0.0
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/CHANGELOG.md +40 -0
- package/README.md +52 -9
- package/dist/module.json +1 -1
- package/dist/runtime/ai-sdk/index.d.ts +33 -26
- package/dist/runtime/ai-sdk/index.js +34 -16
- package/dist/runtime/components/ForgeProvidersPanel.d.vue.ts +13 -0
- package/dist/runtime/components/ForgeProvidersPanel.vue +461 -78
- package/dist/runtime/components/ForgeProvidersPanel.vue.d.ts +13 -0
- package/dist/runtime/editor/index.d.ts +55 -16
- package/dist/runtime/editor/index.js +82 -40
- package/dist/runtime/model/selector.d.ts +16 -0
- package/dist/runtime/model/selector.js +12 -0
- package/dist/runtime/server/credential-store.d.ts +16 -4
- package/dist/runtime/server/credential-store.js +3 -4
- package/dist/runtime/server/env-layers.d.ts +1 -1
- package/dist/runtime/server/env-layers.js +3 -3
- package/dist/runtime/server/index.d.ts +9 -8
- package/dist/runtime/server/index.js +3 -3
- package/dist/runtime/server/provider-discovery.d.ts +28 -8
- package/dist/runtime/server/provider-discovery.js +57 -33
- package/dist/runtime/server/provider-layers.d.ts +1 -1
- package/dist/runtime/server/provider-layers.js +3 -5
- package/dist/runtime/server/provider-service.d.ts +26 -2
- package/dist/runtime/server/provider-service.js +16 -1
- package/dist/runtime/server/provider-write.d.ts +26 -10
- package/dist/runtime/server/provider-write.js +75 -7
- package/dist/runtime/server/voice-provider.d.ts +61 -0
- package/dist/runtime/server/voice-provider.js +31 -0
- package/dist/runtime/shared/data-zone.d.ts +68 -0
- package/dist/runtime/shared/data-zone.js +79 -0
- package/dist/runtime/shared/harness-providers.d.ts +42 -3
- package/dist/runtime/shared/harness-providers.js +31 -8
- package/dist/runtime/shared/harnesses.d.ts +7 -0
- package/dist/runtime/shared/harnesses.js +18 -0
- package/dist/runtime/shared/provider-accounts.d.ts +215 -0
- package/dist/runtime/shared/provider-accounts.js +255 -0
- package/dist/runtime/shared/provider-registry.d.ts +19 -182
- package/dist/runtime/shared/provider-registry.js +25 -65
- package/dist/runtime/vue/index.d.ts +2 -0
- package/dist/runtime/vue/index.js +2 -0
- package/dist/runtime/vue/providers-panel-model.d.ts +98 -5
- package/dist/runtime/vue/providers-panel-model.js +13 -1
- package/dist/runtime/vue/useProvidersPanel.d.ts +66 -5
- package/dist/runtime/vue/useProvidersPanel.js +417 -29
- package/dist/runtime/{server → workspaces}/harness-settings.d.ts +51 -13
- package/dist/runtime/{server → workspaces}/harness-settings.js +93 -9
- package/dist/runtime/workspaces/index.d.ts +16 -0
- package/dist/runtime/workspaces/index.js +5 -0
- package/dist/runtime/workspaces/language-model.d.ts +45 -0
- package/dist/runtime/workspaces/language-model.js +26 -0
- package/dist/runtime/workspaces/model-endpoint.d.ts +38 -0
- package/dist/runtime/workspaces/model-endpoint.js +54 -0
- package/dist/runtime/workspaces/provider-settings.d.ts +35 -0
- package/dist/runtime/{server → workspaces}/provider-settings.js +0 -31
- package/dist/runtime/{server → workspaces}/settings-defaults.d.ts +1 -1
- package/dist/runtime/{shared → workspaces}/settings-types.d.ts +11 -4
- package/package.json +11 -2
- package/src/runtime/ai-sdk/index.ts +88 -47
- package/src/runtime/components/ForgeProvidersPanel.vue +477 -78
- package/src/runtime/editor/index.ts +158 -63
- package/src/runtime/model/selector.ts +31 -0
- package/src/runtime/server/credential-store.ts +20 -10
- package/src/runtime/server/env-layers.ts +4 -4
- package/src/runtime/server/index.ts +9 -8
- package/src/runtime/server/provider-discovery.ts +114 -48
- package/src/runtime/server/provider-layers.ts +6 -6
- package/src/runtime/server/provider-service.ts +53 -1
- package/src/runtime/server/provider-write.ts +132 -18
- package/src/runtime/server/voice-provider.ts +110 -0
- package/src/runtime/shared/data-zone.ts +179 -0
- package/src/runtime/shared/harness-providers.ts +78 -7
- package/src/runtime/shared/harnesses.ts +25 -0
- package/src/runtime/shared/provider-accounts.ts +526 -0
- package/src/runtime/shared/provider-registry.ts +82 -256
- package/src/runtime/vue/index.ts +2 -0
- package/src/runtime/vue/providers-panel-model.ts +123 -5
- package/src/runtime/vue/useProvidersPanel.ts +589 -37
- package/src/runtime/{server → workspaces}/harness-settings.ts +166 -22
- package/src/runtime/workspaces/index.ts +17 -0
- package/src/runtime/workspaces/language-model.ts +82 -0
- package/src/runtime/workspaces/model-endpoint.ts +100 -0
- package/src/runtime/workspaces/provider-settings.ts +96 -0
- package/src/runtime/{server → workspaces}/settings-defaults.ts +1 -1
- package/src/runtime/{shared → workspaces}/settings-types.ts +11 -4
- package/dist/runtime/server/provider-settings.d.ts +0 -78
- package/src/runtime/server/provider-settings.ts +0 -192
- /package/dist/runtime/{server → workspaces}/settings-defaults.js +0 -0
- /package/dist/runtime/{shared → workspaces}/settings-types.js +0 -0
|
@@ -71,10 +71,54 @@
|
|
|
71
71
|
variant="subtle"
|
|
72
72
|
color="neutral"
|
|
73
73
|
>{{ row.badge }}</UBadge>
|
|
74
|
+
<UBadge
|
|
75
|
+
:data-testid="`provider-zone-${row.id}`"
|
|
76
|
+
size="sm"
|
|
77
|
+
variant="outline"
|
|
78
|
+
:color="row.zone === 'transfer' ? 'warning' : 'neutral'"
|
|
79
|
+
:title="`Data zone: ${row.zoneLabel}`"
|
|
80
|
+
>{{ row.zoneLabel }}</UBadge>
|
|
81
|
+
<button
|
|
82
|
+
v-if="row.modelsChip && row.canFilterModels"
|
|
83
|
+
type="button"
|
|
84
|
+
class="rounded-md border border-surface-border px-1.5 py-0.5 text-[10px] text-foreground-muted hover:border-primary-500"
|
|
85
|
+
:data-testid="`provider-models-chip-${row.id}`"
|
|
86
|
+
@click="openModelsEditor(row)"
|
|
87
|
+
>{{ row.modelsChip }}</button>
|
|
88
|
+
<UBadge
|
|
89
|
+
v-else-if="row.modelsChip"
|
|
90
|
+
:data-testid="`provider-models-chip-${row.id}`"
|
|
91
|
+
size="sm"
|
|
92
|
+
variant="subtle"
|
|
93
|
+
color="neutral"
|
|
94
|
+
>{{ row.modelsChip }}</UBadge>
|
|
74
95
|
<Icon v-if="row.locked" name="lucide:lock" class="text-foreground-subtle shrink-0" />
|
|
75
96
|
</div>
|
|
76
97
|
<p class="font-mono text-foreground-subtle truncate" :title="row.baseUrl ?? void 0">{{ rowMeta(row) }}</p>
|
|
98
|
+
<p v-if="row.accountLine" :data-testid="`provider-account-${row.id}`" class="text-foreground-muted">{{ row.accountLine }}</p>
|
|
77
99
|
<p v-if="row.inheritedNote" class="text-foreground-subtle italic">{{ row.inheritedNote }}</p>
|
|
100
|
+
<p
|
|
101
|
+
v-if="row.blockedNote"
|
|
102
|
+
:data-testid="`provider-blocked-${row.id}`"
|
|
103
|
+
class="text-foreground-subtle italic"
|
|
104
|
+
>{{ row.blockedNote }}</p>
|
|
105
|
+
<p
|
|
106
|
+
v-if="row.expiry"
|
|
107
|
+
:data-testid="`provider-expiry-${row.id}`"
|
|
108
|
+
class="flex items-center gap-1"
|
|
109
|
+
:class="row.expiry.expired || row.expiry.days < 7 ? 'text-amber-600 dark:text-amber-400' : 'text-foreground-subtle'"
|
|
110
|
+
>
|
|
111
|
+
{{ row.expiry.text }}
|
|
112
|
+
<template v-if="row.canRenew">
|
|
113
|
+
·
|
|
114
|
+
<button
|
|
115
|
+
type="button"
|
|
116
|
+
class="underline hover:text-foreground"
|
|
117
|
+
:data-testid="`provider-renew-${row.id}`"
|
|
118
|
+
@click="openSignIn(row)"
|
|
119
|
+
>Renew</button>
|
|
120
|
+
</template>
|
|
121
|
+
</p>
|
|
78
122
|
</div>
|
|
79
123
|
<div class="flex items-center gap-1 shrink-0">
|
|
80
124
|
<UButton
|
|
@@ -145,6 +189,16 @@
|
|
|
145
189
|
{{ row.status }}
|
|
146
190
|
</p>
|
|
147
191
|
|
|
192
|
+
<USwitch
|
|
193
|
+
v-if="row.zone === 'eu_option'"
|
|
194
|
+
:model-value="row.euProcessing === true"
|
|
195
|
+
:disabled="!row.canSetEuProcessing || rowBusy === row.id"
|
|
196
|
+
label="EU processing is on for this account"
|
|
197
|
+
size="xs"
|
|
198
|
+
:data-testid="`provider-eu-processing-${row.id}`"
|
|
199
|
+
@update:model-value="setEuProcessing(row, Boolean($event))"
|
|
200
|
+
/>
|
|
201
|
+
|
|
148
202
|
<!-- Inline key editor -->
|
|
149
203
|
<div
|
|
150
204
|
v-if="keyEditing === row.id"
|
|
@@ -203,6 +257,67 @@
|
|
|
203
257
|
</div>
|
|
204
258
|
</div>
|
|
205
259
|
|
|
260
|
+
<!-- Models allowed here -->
|
|
261
|
+
<div
|
|
262
|
+
v-if="modelsEditing === row.id"
|
|
263
|
+
:data-testid="`provider-models-${row.id}`"
|
|
264
|
+
class="space-y-1.5 p-2 rounded-lg border border-surface-border bg-surface-ground/50"
|
|
265
|
+
>
|
|
266
|
+
<p class="font-medium text-foreground">Models allowed here</p>
|
|
267
|
+
<URadioGroup
|
|
268
|
+
:model-value="modelsForm.mode"
|
|
269
|
+
:items="MODELS_MODE_ITEMS"
|
|
270
|
+
orientation="horizontal"
|
|
271
|
+
size="xs"
|
|
272
|
+
:data-testid="`provider-models-mode-${row.id}`"
|
|
273
|
+
@update:model-value="setModelsMode($event)"
|
|
274
|
+
/>
|
|
275
|
+
<template v-if="modelsForm.mode === 'only'">
|
|
276
|
+
<UInput
|
|
277
|
+
v-model="modelsForm.query"
|
|
278
|
+
placeholder="Search models"
|
|
279
|
+
size="xs"
|
|
280
|
+
:data-testid="`provider-models-search-${row.id}`"
|
|
281
|
+
class="w-full bg-surface-ground border-surface-border text-foreground text-xs"
|
|
282
|
+
/>
|
|
283
|
+
<p v-if="modelsLoading" class="text-foreground-subtle italic">Listing models…</p>
|
|
284
|
+
<p
|
|
285
|
+
v-if="modelsListMessage"
|
|
286
|
+
:data-testid="`provider-models-message-${row.id}`"
|
|
287
|
+
class="text-foreground-subtle italic"
|
|
288
|
+
>{{ modelsListMessage }}</p>
|
|
289
|
+
<div class="max-h-48 overflow-y-auto space-y-1">
|
|
290
|
+
<UCheckbox
|
|
291
|
+
v-for="modelId in modelChoices"
|
|
292
|
+
:key="modelId"
|
|
293
|
+
:model-value="modelsForm.selected.includes(modelId)"
|
|
294
|
+
:label="modelId"
|
|
295
|
+
size="xs"
|
|
296
|
+
:data-testid="`provider-model-${row.id}-${modelId}`"
|
|
297
|
+
@update:model-value="toggleModel(modelId, Boolean($event))"
|
|
298
|
+
/>
|
|
299
|
+
</div>
|
|
300
|
+
<p class="text-foreground-subtle">
|
|
301
|
+
{{ modelsForm.selected.length === 0 ? "No model ticked: this provider will run no model." : `${modelsForm.selected.length} ticked.` }}
|
|
302
|
+
</p>
|
|
303
|
+
</template>
|
|
304
|
+
<p
|
|
305
|
+
v-if="modelsError"
|
|
306
|
+
:data-testid="`provider-models-error-${row.id}`"
|
|
307
|
+
class="text-error-500"
|
|
308
|
+
>{{ modelsError }}</p>
|
|
309
|
+
<div class="flex justify-end gap-1">
|
|
310
|
+
<UButton size="xs" variant="ghost" :data-testid="`provider-models-cancel-${row.id}`" @click="cancelModelsEditor()">Cancel</UButton>
|
|
311
|
+
<UButton
|
|
312
|
+
size="xs"
|
|
313
|
+
color="primary"
|
|
314
|
+
:loading="rowBusy === row.id"
|
|
315
|
+
:data-testid="`provider-models-save-${row.id}`"
|
|
316
|
+
@click="saveModelsEditor()"
|
|
317
|
+
>Save</UButton>
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
|
|
206
321
|
<p
|
|
207
322
|
v-for="notice in row.overrideNotices"
|
|
208
323
|
:key="notice"
|
|
@@ -228,7 +343,7 @@
|
|
|
228
343
|
:open="dialogVisible"
|
|
229
344
|
:title="dialogTitle"
|
|
230
345
|
:ui="{
|
|
231
|
-
content: 'bg-surface-card border border-surface-border sm:max-w-[
|
|
346
|
+
content: 'bg-surface-card border border-surface-border sm:max-w-[520px]',
|
|
232
347
|
header: 'bg-surface-card border-b border-surface-border text-foreground',
|
|
233
348
|
body: 'bg-surface-card p-5',
|
|
234
349
|
footer: 'bg-surface-ground/50 border-t border-surface-border p-3 justify-end'
|
|
@@ -237,104 +352,323 @@
|
|
|
237
352
|
>
|
|
238
353
|
<template #body>
|
|
239
354
|
<div data-testid="provider-dialog" class="space-y-4">
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
class="
|
|
250
|
-
|
|
251
|
-
|
|
355
|
+
<!-- Step 1: library -->
|
|
356
|
+
<div v-if="dialogStep === 'library'" data-testid="provider-library" class="space-y-3">
|
|
357
|
+
<p class="text-xs text-foreground-muted">Which provider do you want to add?</p>
|
|
358
|
+
<button
|
|
359
|
+
type="button"
|
|
360
|
+
data-testid="provider-library-azure"
|
|
361
|
+
class="w-full text-left p-3 rounded-lg border border-surface-border hover:border-primary-500 hover:bg-primary-500/5 transition-colors"
|
|
362
|
+
@click="chooseLibrary(AZURE_ACCOUNT)"
|
|
363
|
+
>
|
|
364
|
+
<span class="flex items-center gap-2">
|
|
365
|
+
<Icon name="lucide:cloud" class="text-primary-500 shrink-0" />
|
|
366
|
+
<span class="text-sm font-medium text-foreground">Microsoft Azure</span>
|
|
367
|
+
</span>
|
|
368
|
+
<span class="block text-xs text-foreground-muted mt-1">
|
|
369
|
+
Azure OpenAI and Claude on Foundry from one Azure resource, with Entra ID or an API key.
|
|
370
|
+
</span>
|
|
371
|
+
</button>
|
|
372
|
+
<div class="grid grid-cols-2 gap-2 max-h-72 overflow-y-auto">
|
|
373
|
+
<button
|
|
374
|
+
v-for="preset in libraryPresets"
|
|
375
|
+
:key="preset.id"
|
|
376
|
+
type="button"
|
|
377
|
+
:data-testid="`provider-library-${preset.id}`"
|
|
378
|
+
class="text-left p-2 rounded-lg border border-surface-border hover:border-primary-500 hover:bg-primary-500/5 transition-colors"
|
|
379
|
+
@click="chooseLibrary(preset.id)"
|
|
380
|
+
>
|
|
381
|
+
<span class="block text-xs font-medium text-foreground truncate">{{ preset.label }}</span>
|
|
382
|
+
<span class="block text-[10px] text-foreground-subtle">{{ libraryHint(preset) }}</span>
|
|
383
|
+
</button>
|
|
384
|
+
<button
|
|
385
|
+
type="button"
|
|
386
|
+
data-testid="provider-library-custom"
|
|
387
|
+
class="text-left p-2 rounded-lg border border-dashed border-surface-border hover:border-primary-500 transition-colors"
|
|
388
|
+
@click="chooseLibrary(CUSTOM_PRESET)"
|
|
389
|
+
>
|
|
390
|
+
<span class="block text-xs font-medium text-foreground">Custom</span>
|
|
391
|
+
<span class="block text-[10px] text-foreground-subtle">Any endpoint, by base URL</span>
|
|
392
|
+
</button>
|
|
393
|
+
</div>
|
|
252
394
|
</div>
|
|
253
395
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
396
|
+
<!-- Step 2: account -->
|
|
397
|
+
<template v-else-if="dialogStep === 'account'">
|
|
398
|
+
<!-- Microsoft Azure account -->
|
|
399
|
+
<template v-if="isAzureAccount">
|
|
400
|
+
<div class="flex flex-col gap-1">
|
|
401
|
+
<label class="text-xs text-foreground-muted font-medium">
|
|
402
|
+
Resource name <span class="text-error-500">*</span>
|
|
403
|
+
</label>
|
|
404
|
+
<UInput
|
|
405
|
+
v-model="azureForm.resource"
|
|
406
|
+
placeholder="contoso-ai"
|
|
407
|
+
data-testid="provider-azure-resource"
|
|
408
|
+
class="w-full bg-surface-ground border-surface-border text-foreground text-sm font-mono"
|
|
409
|
+
/>
|
|
410
|
+
<p class="text-[10px] text-foreground-subtle">
|
|
411
|
+
The <code class="font-mono">{{ azureForm.resource.trim() || "name" }}</code> of
|
|
412
|
+
<code class="font-mono">{{ azureForm.resource.trim() || "name" }}.openai.azure.com</code>; shared by every entry this account creates.
|
|
413
|
+
</p>
|
|
414
|
+
</div>
|
|
266
415
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
416
|
+
<div class="space-y-2">
|
|
417
|
+
<p class="text-xs text-foreground-muted font-medium">What do you want to use?</p>
|
|
418
|
+
<UCheckbox
|
|
419
|
+
v-model="azureForm.useOpenAI"
|
|
420
|
+
label="Azure OpenAI"
|
|
421
|
+
description="GPT models you deployed on the resource."
|
|
422
|
+
data-testid="provider-azure-openai"
|
|
423
|
+
/>
|
|
424
|
+
<div v-if="azureForm.useOpenAI" class="ml-6 space-y-1.5" data-testid="provider-azure-deployments">
|
|
425
|
+
<p class="text-[10px] text-foreground-subtle">Deployment name → model id the pickers show (blank: the deployment name).</p>
|
|
426
|
+
<div
|
|
427
|
+
v-for="(row, index) in azureForm.deployments"
|
|
428
|
+
:key="index"
|
|
429
|
+
class="flex items-center gap-1.5"
|
|
430
|
+
>
|
|
431
|
+
<UInput
|
|
432
|
+
v-model="row.deployment"
|
|
433
|
+
placeholder="gpt-5-eu"
|
|
434
|
+
:aria-label="`Deployment ${index + 1}`"
|
|
435
|
+
:data-testid="`provider-azure-deployment-${index}`"
|
|
436
|
+
class="flex-1 bg-surface-ground border-surface-border text-foreground text-xs font-mono"
|
|
437
|
+
/>
|
|
438
|
+
<Icon name="lucide:arrow-right" class="text-foreground-subtle shrink-0" />
|
|
439
|
+
<UInput
|
|
440
|
+
v-model="row.model"
|
|
441
|
+
placeholder="gpt-5"
|
|
442
|
+
:aria-label="`Model id ${index + 1}`"
|
|
443
|
+
:data-testid="`provider-azure-model-${index}`"
|
|
444
|
+
class="flex-1 bg-surface-ground border-surface-border text-foreground text-xs font-mono"
|
|
445
|
+
/>
|
|
446
|
+
<UButton
|
|
447
|
+
size="xs"
|
|
448
|
+
variant="ghost"
|
|
449
|
+
:aria-label="`Remove deployment ${index + 1}`"
|
|
450
|
+
:data-testid="`provider-azure-deployment-remove-${index}`"
|
|
451
|
+
@click="removeDeploymentRow(index)"
|
|
452
|
+
>
|
|
453
|
+
<Icon name="lucide:x" class="w-3.5 h-3.5" />
|
|
454
|
+
</UButton>
|
|
455
|
+
</div>
|
|
456
|
+
<UButton size="xs" variant="ghost" data-testid="provider-azure-deployment-add" @click="addDeploymentRow()">
|
|
457
|
+
<Icon name="lucide:plus" class="mr-1.5" />Add deployment
|
|
458
|
+
</UButton>
|
|
459
|
+
<div class="flex flex-col gap-1">
|
|
460
|
+
<label class="text-xs text-foreground-muted font-medium">API version</label>
|
|
461
|
+
<UInput
|
|
462
|
+
v-model="azureForm.apiVersion"
|
|
463
|
+
:disabled="!props.expert"
|
|
464
|
+
data-testid="provider-azure-api-version"
|
|
465
|
+
class="w-full bg-surface-ground border-surface-border text-foreground text-xs font-mono"
|
|
466
|
+
/>
|
|
467
|
+
<p v-if="!props.expert" class="text-[10px] text-foreground-subtle">Editable in expert mode.</p>
|
|
468
|
+
</div>
|
|
469
|
+
</div>
|
|
470
|
+
<UCheckbox
|
|
471
|
+
v-model="azureForm.useFoundry"
|
|
472
|
+
label="Claude on Foundry"
|
|
473
|
+
description="Anthropic models deployed through Azure AI Foundry on the same resource."
|
|
474
|
+
data-testid="provider-azure-foundry"
|
|
475
|
+
/>
|
|
476
|
+
</div>
|
|
477
|
+
</template>
|
|
282
478
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
479
|
+
<template v-else>
|
|
480
|
+
<div class="flex flex-col gap-1">
|
|
481
|
+
<label class="text-xs text-foreground-muted font-medium">
|
|
482
|
+
Preset <span class="text-error-500">*</span>
|
|
483
|
+
</label>
|
|
484
|
+
<USelect
|
|
485
|
+
:model-value="form.preset"
|
|
486
|
+
:items="presetOptions"
|
|
487
|
+
placeholder="Choose a preset"
|
|
488
|
+
data-testid="provider-preset"
|
|
489
|
+
class="w-full bg-surface-ground border-surface-border text-foreground text-sm"
|
|
490
|
+
@update:model-value="selectPreset(String($event ?? ''))"
|
|
491
|
+
/>
|
|
492
|
+
</div>
|
|
294
493
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
</
|
|
494
|
+
<div v-if="showSurface" class="flex flex-col gap-1">
|
|
495
|
+
<label class="text-xs text-foreground-muted font-medium">
|
|
496
|
+
Surface <span class="text-error-500">*</span>
|
|
497
|
+
</label>
|
|
498
|
+
<USelect
|
|
499
|
+
v-model="form.surface"
|
|
500
|
+
:items="surfaceOptions"
|
|
501
|
+
placeholder="Wire protocol"
|
|
502
|
+
data-testid="provider-surface"
|
|
503
|
+
class="w-full bg-surface-ground border-surface-border text-foreground text-sm"
|
|
504
|
+
/>
|
|
505
|
+
</div>
|
|
506
|
+
|
|
507
|
+
<div class="flex flex-col gap-1">
|
|
508
|
+
<label class="text-xs text-foreground-muted font-medium">
|
|
509
|
+
ID <span class="text-error-500">*</span>
|
|
510
|
+
</label>
|
|
511
|
+
<UInput
|
|
512
|
+
v-model="form.id"
|
|
513
|
+
:disabled="editing !== null"
|
|
514
|
+
placeholder="openrouter"
|
|
515
|
+
data-testid="provider-id"
|
|
516
|
+
class="w-full bg-surface-ground border-surface-border text-foreground text-sm font-mono"
|
|
517
|
+
:class="editing !== null ? 'opacity-60 cursor-not-allowed' : ''"
|
|
518
|
+
/>
|
|
519
|
+
<p v-if="idError" class="text-xs text-error-500">{{ idError }}</p>
|
|
520
|
+
<p v-else class="text-[10px] text-foreground-subtle">The left half of a model selector: <code class="font-mono"><id>/<model></code>.</p>
|
|
521
|
+
</div>
|
|
522
|
+
|
|
523
|
+
<div class="flex flex-col gap-1">
|
|
524
|
+
<label class="text-xs text-foreground-muted font-medium">
|
|
525
|
+
Base URL <span v-if="isCustom" class="text-error-500">*</span>
|
|
526
|
+
</label>
|
|
527
|
+
<UInput
|
|
528
|
+
v-model="form.baseUrl"
|
|
529
|
+
:placeholder="baseUrlPlaceholder"
|
|
530
|
+
data-testid="provider-base-url"
|
|
531
|
+
class="w-full bg-surface-ground border-surface-border text-foreground text-sm font-mono"
|
|
532
|
+
/>
|
|
533
|
+
</div>
|
|
534
|
+
|
|
535
|
+
<div v-for="field in paramFields" :key="field.key" class="flex flex-col gap-1">
|
|
536
|
+
<label class="text-xs text-foreground-muted font-medium">
|
|
537
|
+
{{ field.label }} <span v-if="field.required" class="text-error-500">*</span>
|
|
538
|
+
</label>
|
|
539
|
+
<UInput
|
|
540
|
+
v-model="form.params[field.key]"
|
|
541
|
+
:disabled="!field.editable"
|
|
542
|
+
:data-testid="`provider-param-${field.key}`"
|
|
543
|
+
class="w-full bg-surface-ground border-surface-border text-foreground text-sm font-mono"
|
|
544
|
+
/>
|
|
545
|
+
<p v-if="!field.editable" class="text-[10px] text-foreground-subtle">Editable in expert mode.</p>
|
|
546
|
+
</div>
|
|
547
|
+
</template>
|
|
548
|
+
|
|
549
|
+
<!-- How do you sign in? -->
|
|
550
|
+
<div v-if="showSignIn" data-testid="provider-signin" class="space-y-1.5">
|
|
551
|
+
<p class="text-xs text-foreground-muted font-medium">How do you sign in?</p>
|
|
552
|
+
<div class="flex flex-wrap gap-1.5" role="radiogroup" aria-label="How do you sign in?">
|
|
553
|
+
<UButton
|
|
554
|
+
v-for="option in signInOptions"
|
|
555
|
+
:key="option.id"
|
|
556
|
+
size="xs"
|
|
557
|
+
role="radio"
|
|
558
|
+
:aria-checked="form.signIn === option.id"
|
|
559
|
+
:variant="form.signIn === option.id ? 'solid' : 'outline'"
|
|
560
|
+
:color="form.signIn === option.id ? 'primary' : 'neutral'"
|
|
561
|
+
:data-testid="`provider-signin-${option.id}`"
|
|
562
|
+
@click="selectSignIn(option.id)"
|
|
563
|
+
>{{ option.label }}</UButton>
|
|
564
|
+
</div>
|
|
565
|
+
<p v-if="signInOption" class="text-[10px] text-foreground-subtle leading-relaxed">{{ signInOption.description }}</p>
|
|
307
566
|
</div>
|
|
308
567
|
|
|
309
|
-
<
|
|
310
|
-
<
|
|
568
|
+
<template v-if="showCredentialFields">
|
|
569
|
+
<div class="flex flex-col gap-1">
|
|
570
|
+
<label class="text-xs text-foreground-muted font-medium">Credential environment variable</label>
|
|
571
|
+
<UInput
|
|
572
|
+
v-model="form.envName"
|
|
573
|
+
:placeholder="defaultEnvPlaceholder"
|
|
574
|
+
data-testid="provider-credential-env"
|
|
575
|
+
class="w-full bg-surface-ground border-surface-border text-foreground text-sm font-mono"
|
|
576
|
+
/>
|
|
577
|
+
<p class="text-[10px] text-foreground-subtle">
|
|
578
|
+
Stored as <code class="font-mono">env:{{ form.envName.trim() || defaultEnvPlaceholder }}</code> — the key itself stays in the environment.
|
|
579
|
+
</p>
|
|
580
|
+
</div>
|
|
581
|
+
|
|
582
|
+
<div v-if="canSetValue" class="flex flex-col gap-1">
|
|
583
|
+
<label class="text-xs text-foreground-muted font-medium">Set value now</label>
|
|
584
|
+
<UInput
|
|
585
|
+
v-model="form.secret"
|
|
586
|
+
type="password"
|
|
587
|
+
autocomplete="off"
|
|
588
|
+
placeholder="Optional — written to this scope's .env"
|
|
589
|
+
data-testid="provider-set-value"
|
|
590
|
+
class="w-full bg-surface-ground border-surface-border text-foreground text-sm font-mono"
|
|
591
|
+
/>
|
|
592
|
+
</div>
|
|
593
|
+
</template>
|
|
594
|
+
|
|
595
|
+
<div v-if="showReferenceField" class="flex flex-col gap-1">
|
|
596
|
+
<label class="text-xs text-foreground-muted font-medium">
|
|
597
|
+
Credential reference <span class="text-error-500">*</span>
|
|
598
|
+
</label>
|
|
311
599
|
<UInput
|
|
312
|
-
v-model="form.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
placeholder="Optional — written to this scope's .env"
|
|
316
|
-
data-testid="provider-set-value"
|
|
600
|
+
v-model="form.credentialRef"
|
|
601
|
+
placeholder="env:NAME · keyring:forge/name · cmd:… · platform:…"
|
|
602
|
+
data-testid="provider-dialog-credential-ref"
|
|
317
603
|
class="w-full bg-surface-ground border-surface-border text-foreground text-sm font-mono"
|
|
318
604
|
/>
|
|
319
605
|
</div>
|
|
320
606
|
</template>
|
|
321
607
|
|
|
608
|
+
<!-- Step 3: done, the created entries tested -->
|
|
609
|
+
<div v-else data-testid="provider-done" class="space-y-2">
|
|
610
|
+
<p class="text-xs text-foreground-muted">
|
|
611
|
+
{{ createdIds.length === 1 ? "Added" : `Added ${createdIds.length} providers` }}. Testing the sign-in:
|
|
612
|
+
</p>
|
|
613
|
+
<div
|
|
614
|
+
v-for="id in createdIds"
|
|
615
|
+
:key="id"
|
|
616
|
+
:data-testid="`provider-created-${id}`"
|
|
617
|
+
class="flex items-start gap-2 p-2 rounded-lg border border-surface-border text-xs"
|
|
618
|
+
>
|
|
619
|
+
<span class="font-mono text-foreground shrink-0">{{ id }}</span>
|
|
620
|
+
<span v-if="testBusy === id" class="text-foreground-subtle italic">testing…</span>
|
|
621
|
+
<span
|
|
622
|
+
v-else-if="testResults[id]"
|
|
623
|
+
:data-testid="`provider-created-result-${id}`"
|
|
624
|
+
:class="testResults[id]?.ok ? 'text-success-500' : 'text-error-500'"
|
|
625
|
+
>{{ testResults[id]?.message }}</span>
|
|
626
|
+
<UButton
|
|
627
|
+
v-if="testBusy !== id"
|
|
628
|
+
size="xs"
|
|
629
|
+
variant="ghost"
|
|
630
|
+
class="ml-auto"
|
|
631
|
+
:disabled="testBusy !== null"
|
|
632
|
+
:data-testid="`provider-created-test-${id}`"
|
|
633
|
+
@click="testProvider({ id })"
|
|
634
|
+
>Test again</UButton>
|
|
635
|
+
</div>
|
|
636
|
+
</div>
|
|
637
|
+
|
|
322
638
|
<p v-if="dialogError" data-testid="provider-dialog-error" class="text-sm text-error-500">{{ dialogError }}</p>
|
|
323
639
|
</div>
|
|
324
640
|
</template>
|
|
325
641
|
|
|
326
642
|
<template #footer>
|
|
327
643
|
<div class="flex gap-2 justify-end w-full">
|
|
328
|
-
<UButton size="sm" variant="ghost" @click="dialogVisible = false">Cancel</UButton>
|
|
329
644
|
<UButton
|
|
645
|
+
v-if="!editing && dialogStep === 'account'"
|
|
330
646
|
size="sm"
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
@click="
|
|
647
|
+
variant="ghost"
|
|
648
|
+
class="mr-auto"
|
|
649
|
+
data-testid="provider-back"
|
|
650
|
+
@click="backToLibrary()"
|
|
335
651
|
>
|
|
336
|
-
<Icon name="lucide:
|
|
652
|
+
<Icon name="lucide:arrow-left" class="mr-1.5" />Back
|
|
337
653
|
</UButton>
|
|
654
|
+
<template v-if="dialogStep === 'done'">
|
|
655
|
+
<UButton size="sm" color="primary" data-testid="provider-done-close" @click="finishDialog()">
|
|
656
|
+
<Icon name="lucide:check" class="mr-1.5" />Done
|
|
657
|
+
</UButton>
|
|
658
|
+
</template>
|
|
659
|
+
<template v-else>
|
|
660
|
+
<UButton size="sm" variant="ghost" @click="dialogVisible = false">Cancel</UButton>
|
|
661
|
+
<UButton
|
|
662
|
+
v-if="dialogStep === 'account'"
|
|
663
|
+
size="sm"
|
|
664
|
+
color="primary"
|
|
665
|
+
data-testid="provider-save"
|
|
666
|
+
:loading="saving"
|
|
667
|
+
@click="saveProvider"
|
|
668
|
+
>
|
|
669
|
+
<Icon name="lucide:check" class="mr-1.5" />{{ editing ? "Save" : isAzureAccount ? "Add account" : "Add" }}
|
|
670
|
+
</UButton>
|
|
671
|
+
</template>
|
|
338
672
|
</div>
|
|
339
673
|
</template>
|
|
340
674
|
</UModal>
|
|
@@ -375,7 +709,12 @@
|
|
|
375
709
|
|
|
376
710
|
<script setup>
|
|
377
711
|
import { computed } from "vue";
|
|
378
|
-
import {
|
|
712
|
+
import {
|
|
713
|
+
AZURE_ACCOUNT,
|
|
714
|
+
CUSTOM_PRESET,
|
|
715
|
+
providersPanelNote,
|
|
716
|
+
providersPanelRowMeta as rowMeta
|
|
717
|
+
} from "../vue/providers-panel-model";
|
|
379
718
|
import { useProvidersPanel } from "../vue/useProvidersPanel";
|
|
380
719
|
const props = defineProps({
|
|
381
720
|
client: { type: Object, required: false },
|
|
@@ -386,9 +725,23 @@ const props = defineProps({
|
|
|
386
725
|
environmentEnabled: { type: Boolean, required: false, default: void 0 },
|
|
387
726
|
layer: { type: String, required: true },
|
|
388
727
|
readOnly: { type: Boolean, required: false },
|
|
389
|
-
note: { type: [String, Boolean], required: false, default: void 0 }
|
|
728
|
+
note: { type: [String, Boolean], required: false, default: void 0 },
|
|
729
|
+
expert: { type: Boolean, required: false, default: false },
|
|
730
|
+
dataZoneRule: { type: null, required: false }
|
|
390
731
|
});
|
|
391
732
|
const noteText = computed(() => providersPanelNote(props.note, props.layer));
|
|
733
|
+
const MODELS_MODE_ITEMS = [
|
|
734
|
+
{ label: "All models", value: "all" },
|
|
735
|
+
{ label: "Only these", value: "only" }
|
|
736
|
+
];
|
|
737
|
+
function libraryHint(preset) {
|
|
738
|
+
if (preset.credential.kind === "none") return "Local, no sign-in";
|
|
739
|
+
if (preset.kind === "stt") return "Speech to text";
|
|
740
|
+
if (preset.kind === "tts") return "Text to speech";
|
|
741
|
+
if (preset.kind === "embed") return "Embeddings";
|
|
742
|
+
return "Chat models";
|
|
743
|
+
}
|
|
744
|
+
const defaultEnvPlaceholder = computed(() => defaultEnvName.value ?? "PROVIDER_API_KEY");
|
|
392
745
|
const emit = defineEmits(["changed"]);
|
|
393
746
|
const {
|
|
394
747
|
rows,
|
|
@@ -407,6 +760,24 @@ const {
|
|
|
407
760
|
baseUrlPlaceholder,
|
|
408
761
|
canSetValue,
|
|
409
762
|
showCredentialFields,
|
|
763
|
+
defaultEnvName,
|
|
764
|
+
dialogStep,
|
|
765
|
+
libraryPresets,
|
|
766
|
+
chooseLibrary,
|
|
767
|
+
backToLibrary,
|
|
768
|
+
isAzureAccount,
|
|
769
|
+
signInOptions,
|
|
770
|
+
signInOption,
|
|
771
|
+
selectSignIn,
|
|
772
|
+
showSignIn,
|
|
773
|
+
showReferenceField,
|
|
774
|
+
paramFields,
|
|
775
|
+
azureForm,
|
|
776
|
+
addDeploymentRow,
|
|
777
|
+
removeDeploymentRow,
|
|
778
|
+
createdIds,
|
|
779
|
+
finishDialog,
|
|
780
|
+
openSignIn,
|
|
410
781
|
rowBusy,
|
|
411
782
|
rowError,
|
|
412
783
|
canTest,
|
|
@@ -416,6 +787,18 @@ const {
|
|
|
416
787
|
onCredentialSelect,
|
|
417
788
|
setEnabled,
|
|
418
789
|
resetPatch,
|
|
790
|
+
setEuProcessing,
|
|
791
|
+
modelsEditing,
|
|
792
|
+
modelsForm,
|
|
793
|
+
modelsLoading,
|
|
794
|
+
modelsListMessage,
|
|
795
|
+
modelChoices,
|
|
796
|
+
modelsError,
|
|
797
|
+
openModelsEditor,
|
|
798
|
+
setModelsMode,
|
|
799
|
+
toggleModel,
|
|
800
|
+
cancelModelsEditor,
|
|
801
|
+
saveModelsEditor,
|
|
419
802
|
keyEditing,
|
|
420
803
|
keyForm,
|
|
421
804
|
keyError,
|