refac
This commit is contained in:
@@ -49,6 +49,7 @@
|
||||
let apiType = ''; // '' = chat completions (default), 'responses' = Responses API
|
||||
|
||||
let headers = '';
|
||||
let passthroughParams = '';
|
||||
|
||||
let tags = [];
|
||||
|
||||
@@ -57,12 +58,19 @@
|
||||
|
||||
let loading = false;
|
||||
let showDeleteConfirmDialog = false;
|
||||
let showAdvanced = false;
|
||||
|
||||
const inputClass =
|
||||
'bg-transparent outline-hidden placeholder:text-gray-300 dark:placeholder:text-gray-700';
|
||||
const selectClass =
|
||||
'dark:bg-gray-900 bg-transparent pr-5 outline-hidden placeholder:text-gray-300 dark:placeholder:text-gray-700';
|
||||
|
||||
const parsePassthroughParams = (value: string) =>
|
||||
value
|
||||
.split(',')
|
||||
.map((param) => param.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const verifyOllamaHandler = async () => {
|
||||
// remove trailing slash from url
|
||||
url = url.replace(/\/$/, '');
|
||||
@@ -109,6 +117,7 @@
|
||||
...(provider ? { provider } : {}),
|
||||
...(azure ? { azure: true } : {}),
|
||||
api_version: apiVersion,
|
||||
passthrough_params: parsePassthroughParams(passthroughParams),
|
||||
...(_headers ? { headers: _headers } : {})
|
||||
}
|
||||
},
|
||||
@@ -149,6 +158,7 @@
|
||||
if (azure) {
|
||||
if (!apiVersion) {
|
||||
loading = false;
|
||||
showAdvanced = true;
|
||||
|
||||
toast.error($i18n.t('API Version is required'));
|
||||
return;
|
||||
@@ -163,6 +173,7 @@
|
||||
|
||||
if (modelIds.length === 0) {
|
||||
loading = false;
|
||||
showAdvanced = true;
|
||||
toast.error($i18n.t('Deployment names are required for Azure OpenAI'));
|
||||
return;
|
||||
}
|
||||
@@ -195,6 +206,7 @@
|
||||
connection_type: connectionType,
|
||||
auth_type,
|
||||
headers: headers ? JSON.parse(headers) : undefined,
|
||||
passthrough_params: parsePassthroughParams(passthroughParams),
|
||||
...(provider ? { provider } : {}),
|
||||
...(!ollama && azure ? { azure: true } : {}),
|
||||
...(azure ? { api_version: apiVersion } : {}),
|
||||
@@ -211,6 +223,8 @@
|
||||
key = '';
|
||||
auth_type = 'bearer';
|
||||
prefixId = '';
|
||||
passthroughParams = '';
|
||||
showAdvanced = false;
|
||||
tags = [];
|
||||
modelIds = [];
|
||||
};
|
||||
@@ -228,6 +242,9 @@
|
||||
enable = connection.config?.enable ?? true;
|
||||
tags = connection.config?.tags ?? [];
|
||||
prefixId = connection.config?.prefix_id ?? '';
|
||||
passthroughParams = Array.isArray(connection.config?.passthrough_params)
|
||||
? connection.config.passthrough_params.join(', ')
|
||||
: (connection.config?.passthrough_params ?? '');
|
||||
modelIds = connection.config?.model_ids ?? [];
|
||||
|
||||
if (ollama) {
|
||||
@@ -429,107 +446,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if !direct}
|
||||
<div class="flex gap-2 mt-2">
|
||||
<div class="flex flex-col w-full">
|
||||
<label
|
||||
for="headers-input"
|
||||
class={`mb-0.5 text-xs text-gray-500
|
||||
`}>{$i18n.t('Headers')}</label
|
||||
>
|
||||
|
||||
<div class="flex-1">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'Enter additional headers in JSON format (e.g. {"X-Custom-Header": "value"}'
|
||||
)}
|
||||
>
|
||||
<Textarea
|
||||
className="w-full text-sm outline-hidden"
|
||||
bind:value={headers}
|
||||
placeholder={$i18n.t('Enter additional headers in JSON format')}
|
||||
required={false}
|
||||
minSize={30}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex gap-2 mt-2">
|
||||
<div class="flex flex-col w-full">
|
||||
<label
|
||||
for="prefix-id-input"
|
||||
class={`mb-0.5 text-xs text-gray-500
|
||||
`}>{$i18n.t('Prefix ID')}</label
|
||||
>
|
||||
|
||||
<div class="flex-1">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable'
|
||||
)}
|
||||
>
|
||||
<input
|
||||
class={`w-full text-sm ${inputClass}`}
|
||||
type="text"
|
||||
id="prefix-id-input"
|
||||
bind:value={prefixId}
|
||||
placeholder={$i18n.t('Prefix ID')}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if !ollama && !direct}
|
||||
<div class="flex flex-row justify-between items-center w-full mt-2">
|
||||
<label
|
||||
for="provider-select"
|
||||
class={`mb-0.5 text-xs text-gray-500
|
||||
`}>{$i18n.t('Provider')}</label
|
||||
>
|
||||
|
||||
<div>
|
||||
<select
|
||||
id="provider-select"
|
||||
bind:value={provider}
|
||||
class="text-xs text-gray-700 dark:text-gray-300 bg-transparent outline-hidden"
|
||||
>
|
||||
<option value="">{$i18n.t('Default')}</option>
|
||||
<option value="azure">{$i18n.t('Azure OpenAI')}</option>
|
||||
<option value="llama.cpp">{$i18n.t('llama.cpp')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if azure}
|
||||
<div class="flex gap-2 mt-2">
|
||||
<div class="flex flex-col w-full">
|
||||
<label
|
||||
for="api-version-input"
|
||||
class={`mb-0.5 text-xs text-gray-500
|
||||
`}>{$i18n.t('API Version')}</label
|
||||
>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
id="api-version-input"
|
||||
class={`w-full text-sm ${inputClass}`}
|
||||
type="text"
|
||||
bind:value={apiVersion}
|
||||
placeholder={$i18n.t('API Version')}
|
||||
autocomplete="off"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !ollama && !direct}
|
||||
<div class="flex flex-row justify-between items-center w-full mt-1">
|
||||
<label
|
||||
@@ -548,19 +464,7 @@
|
||||
class=" text-xs text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{#if apiType === 'responses'}
|
||||
<Tooltip
|
||||
className="flex items-center gap-1"
|
||||
content={$i18n.t(
|
||||
'This feature is currently experimental and may not work as expected.'
|
||||
)}
|
||||
>
|
||||
<span
|
||||
class="inline-flex items-center text-[0.625rem] font-normal uppercase leading-none text-gray-400 dark:text-gray-600"
|
||||
>{$i18n.t('Experimental')}</span
|
||||
>
|
||||
|
||||
{$i18n.t('Responses')}
|
||||
</Tooltip>
|
||||
{$i18n.t('Responses')}
|
||||
{:else}
|
||||
{$i18n.t('Chat Completions')}
|
||||
{/if}
|
||||
@@ -569,111 +473,268 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-col w-full mt-2">
|
||||
<div class="mb-1 flex justify-between">
|
||||
<div
|
||||
class={`mb-0.5 text-xs text-gray-500
|
||||
`}
|
||||
<div class="flex items-center justify-between">
|
||||
<button
|
||||
type="button"
|
||||
class="flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition mt-2"
|
||||
on:click={() => (showAdvanced = !showAdvanced)}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
class="w-3 h-3 transition-transform {showAdvanced ? 'rotate-90' : ''}"
|
||||
>
|
||||
{$i18n.t('Model IDs')}
|
||||
</div>
|
||||
</div>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
{$i18n.t('Advanced')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if modelIds.length > 0}
|
||||
<ul class="flex flex-col">
|
||||
{#each modelIds as modelId, modelIdx}
|
||||
<li class=" flex gap-2 w-full justify-between items-center">
|
||||
<div class=" text-sm flex-1 py-1 rounded-lg">
|
||||
{modelId}
|
||||
</div>
|
||||
<div class="shrink-0">
|
||||
<button
|
||||
aria-label={$i18n.t(`Remove {{MODELID}} from list.`, {
|
||||
MODELID: modelId
|
||||
})}
|
||||
type="button"
|
||||
on:click={() => {
|
||||
modelIds = modelIds.filter((_, idx) => idx !== modelIdx);
|
||||
}}
|
||||
>
|
||||
<Minus strokeWidth="2" className="size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{:else}
|
||||
<div
|
||||
class={`text-gray-500 text-xs text-center py-2 px-10
|
||||
`}
|
||||
>
|
||||
{#if ollama}
|
||||
{$i18n.t('Leave empty to include all models from "{{url}}/api/tags" endpoint', {
|
||||
url: url
|
||||
})}
|
||||
{:else if azure}
|
||||
{$i18n.t('Deployment names are required for Azure OpenAI')}
|
||||
<!-- {$i18n.t('Leave empty to include all models from "{{url}}" endpoint', {
|
||||
url: `${url}/openai/deployments`
|
||||
})} -->
|
||||
{:else}
|
||||
{$i18n.t('Leave empty to include all models from "{{url}}/models" endpoint', {
|
||||
url: url
|
||||
})}
|
||||
{/if}
|
||||
{#if showAdvanced}
|
||||
{#if !direct}
|
||||
<div class="flex gap-2 mt-2">
|
||||
<div class="flex flex-col w-full">
|
||||
<label
|
||||
for="headers-input"
|
||||
class={`mb-0.5 text-xs text-gray-500
|
||||
`}>{$i18n.t('Headers')}</label
|
||||
>
|
||||
|
||||
<div class="flex-1">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'Enter additional headers in JSON format (e.g. {"X-Custom-Header": "value"}'
|
||||
)}
|
||||
>
|
||||
<Textarea
|
||||
className="w-full text-sm outline-hidden"
|
||||
bind:value={headers}
|
||||
placeholder={$i18n.t('Enter additional headers in JSON format')}
|
||||
required={false}
|
||||
minSize={30}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<label class="sr-only" for="add-model-id-input">{$i18n.t('Add a model ID')}</label>
|
||||
<input
|
||||
class={`w-full text-sm ${inputClass} ${modelId ? '' : 'text-gray-500'}`}
|
||||
bind:value={modelId}
|
||||
id="add-model-id-input"
|
||||
placeholder={$i18n.t('Add a model ID')}
|
||||
/>
|
||||
{#if !ollama && !direct}
|
||||
<div class="flex gap-2 mt-2">
|
||||
<div class="flex flex-col w-full">
|
||||
<label
|
||||
for="allowed-passthrough-params-input"
|
||||
class={`mb-0.5 text-xs text-gray-500
|
||||
`}>{$i18n.t('Passthrough params')}</label
|
||||
>
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={$i18n.t('Add')}
|
||||
on:click={() => {
|
||||
addModelHandler();
|
||||
}}
|
||||
>
|
||||
<Plus className="size-3.5" strokeWidth="2" />
|
||||
</button>
|
||||
<div class="flex-1">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'Comma-separated top-level request parameters this upstream may receive without translation. Use * to allow all captured passthrough params.'
|
||||
)}
|
||||
>
|
||||
<input
|
||||
class={`w-full text-sm ${inputClass}`}
|
||||
type="text"
|
||||
id="allowed-passthrough-params-input"
|
||||
bind:value={passthroughParams}
|
||||
placeholder={$i18n.t('thinking, output_config')}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex gap-2 mt-2">
|
||||
<div class="flex flex-col w-full">
|
||||
<label
|
||||
for="prefix-id-input"
|
||||
class={`mb-0.5 text-xs text-gray-500
|
||||
`}>{$i18n.t('Prefix ID')}</label
|
||||
>
|
||||
|
||||
<div class="flex-1">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'Prefix ID is used to avoid conflicts with other connections by adding a prefix to the model IDs - leave empty to disable'
|
||||
)}
|
||||
>
|
||||
<input
|
||||
class={`w-full text-sm ${inputClass}`}
|
||||
type="text"
|
||||
id="prefix-id-input"
|
||||
bind:value={prefixId}
|
||||
placeholder={$i18n.t('Prefix ID')}
|
||||
autocomplete="off"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 mt-2">
|
||||
<div class="flex flex-col w-full">
|
||||
<div
|
||||
class={`mb-0.5 text-xs text-gray-500
|
||||
{#if !ollama && !direct}
|
||||
<div class="flex flex-row justify-between items-center w-full mt-2">
|
||||
<label
|
||||
for="provider-select"
|
||||
class={`mb-0.5 text-xs text-gray-500
|
||||
`}>{$i18n.t('Provider')}</label
|
||||
>
|
||||
|
||||
<div>
|
||||
<select
|
||||
id="provider-select"
|
||||
bind:value={provider}
|
||||
class="text-xs text-gray-700 dark:text-gray-300 bg-transparent outline-hidden"
|
||||
>
|
||||
<option value="">{$i18n.t('Default')}</option>
|
||||
<option value="azure">{$i18n.t('Azure OpenAI')}</option>
|
||||
<option value="llama.cpp">{$i18n.t('llama.cpp')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if azure}
|
||||
<div class="flex gap-2 mt-2">
|
||||
<div class="flex flex-col w-full">
|
||||
<label
|
||||
for="api-version-input"
|
||||
class={`mb-0.5 text-xs text-gray-500
|
||||
`}>{$i18n.t('API Version')}</label
|
||||
>
|
||||
|
||||
<div class="flex-1">
|
||||
<input
|
||||
id="api-version-input"
|
||||
class={`w-full text-sm ${inputClass}`}
|
||||
type="text"
|
||||
bind:value={apiVersion}
|
||||
placeholder={$i18n.t('API Version')}
|
||||
autocomplete="off"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-col w-full mt-2">
|
||||
<div class="mb-1 flex justify-between">
|
||||
<div
|
||||
class={`mb-0.5 text-xs text-gray-500
|
||||
`}
|
||||
>
|
||||
{$i18n.t('Tags')}
|
||||
>
|
||||
{$i18n.t('Model IDs')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if modelIds.length > 0}
|
||||
<ul class="flex flex-col">
|
||||
{#each modelIds as modelId, modelIdx}
|
||||
<li class=" flex gap-2 w-full justify-between items-center">
|
||||
<div class=" text-sm flex-1 py-1 rounded-lg">
|
||||
{modelId}
|
||||
</div>
|
||||
<div class="shrink-0">
|
||||
<button
|
||||
aria-label={$i18n.t(`Remove {{MODELID}} from list.`, {
|
||||
MODELID: modelId
|
||||
})}
|
||||
type="button"
|
||||
on:click={() => {
|
||||
modelIds = modelIds.filter((_, idx) => idx !== modelIdx);
|
||||
}}
|
||||
>
|
||||
<Minus strokeWidth="2" className="size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{:else}
|
||||
<div
|
||||
class={`text-gray-500 text-xs text-center py-2 px-10
|
||||
`}
|
||||
>
|
||||
{#if ollama}
|
||||
{$i18n.t(
|
||||
'Leave empty to include all models from "{{url}}/api/tags" endpoint',
|
||||
{
|
||||
url: url
|
||||
}
|
||||
)}
|
||||
{:else if azure}
|
||||
{$i18n.t('Deployment names are required for Azure OpenAI')}
|
||||
<!-- {$i18n.t('Leave empty to include all models from "{{url}}" endpoint', {
|
||||
url: `${url}/openai/deployments`
|
||||
})} -->
|
||||
{:else}
|
||||
{$i18n.t('Leave empty to include all models from "{{url}}/models" endpoint', {
|
||||
url: url
|
||||
})}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex-1 mt-0.5">
|
||||
<Tags
|
||||
bind:tags
|
||||
on:add={(e) => {
|
||||
tags = [
|
||||
...tags,
|
||||
{
|
||||
name: e.detail
|
||||
}
|
||||
];
|
||||
}}
|
||||
on:delete={(e) => {
|
||||
tags = tags.filter((tag) => tag.name !== e.detail);
|
||||
}}
|
||||
<div class="flex items-center">
|
||||
<label class="sr-only" for="add-model-id-input">{$i18n.t('Add a model ID')}</label>
|
||||
<input
|
||||
class={`w-full text-sm ${inputClass} ${modelId ? '' : 'text-gray-500'}`}
|
||||
bind:value={modelId}
|
||||
id="add-model-id-input"
|
||||
placeholder={$i18n.t('Add a model ID')}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={$i18n.t('Add')}
|
||||
on:click={() => {
|
||||
addModelHandler();
|
||||
}}
|
||||
>
|
||||
<Plus className="size-3.5" strokeWidth="2" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 mt-2">
|
||||
<div class="flex flex-col w-full">
|
||||
<div
|
||||
class={`mb-0.5 text-xs text-gray-500
|
||||
`}
|
||||
>
|
||||
{$i18n.t('Tags')}
|
||||
</div>
|
||||
|
||||
<div class="flex-1 mt-0.5">
|
||||
<Tags
|
||||
bind:tags
|
||||
on:add={(e) => {
|
||||
tags = [
|
||||
...tags,
|
||||
{
|
||||
name: e.detail
|
||||
}
|
||||
];
|
||||
}}
|
||||
on:delete={(e) => {
|
||||
tags = tags.filter((tag) => tag.name !== e.detail);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center pt-3 text-sm font-medium">
|
||||
|
||||
Reference in New Issue
Block a user