refac
This commit is contained in:
@@ -111,12 +111,14 @@
|
||||
import FilesOverlay from './MessageInput/FilesOverlay.svelte';
|
||||
import NotificationToast from '../NotificationToast.svelte';
|
||||
import Spinner from '../common/Spinner.svelte';
|
||||
import Modal from '../common/Modal.svelte';
|
||||
import { isEmbedWindow } from '../common/FullHeightIframe.svelte';
|
||||
import Tooltip from '../common/Tooltip.svelte';
|
||||
import Sidebar from '../icons/Sidebar.svelte';
|
||||
import Image from '../common/Image.svelte';
|
||||
import XMark from '../icons/XMark.svelte';
|
||||
import EmbeddedChatHistoryDropdown from './EmbeddedChatHistoryDropdown.svelte';
|
||||
import InputVariablesModal from './MessageInput/InputVariablesModal.svelte';
|
||||
|
||||
export let chatIdProp = '';
|
||||
export let embedded = false;
|
||||
@@ -367,9 +369,112 @@
|
||||
let chatFiles = [];
|
||||
let files = [];
|
||||
let params = {};
|
||||
let chatVariables = {};
|
||||
let showChatVariablesModal = false;
|
||||
let loadedChatIdProp = '';
|
||||
let currentDraftKey = '';
|
||||
|
||||
const mergeChatVariableSchemas = (modelIds = []) => {
|
||||
const byKey: Record<string, any> = {};
|
||||
const conflicts: any[] = [];
|
||||
|
||||
for (const modelId of modelIds.filter(Boolean)) {
|
||||
const fields =
|
||||
$models.find((model) => model.id === modelId)?.info?.meta?.chat_variables_schema?.fields ??
|
||||
[];
|
||||
for (const rawField of fields) {
|
||||
const field = {
|
||||
...rawField,
|
||||
type: rawField?.type ?? 'text',
|
||||
required: Boolean(rawField?.required)
|
||||
};
|
||||
if (!field?.key) continue;
|
||||
const { required, ...shape } = field;
|
||||
|
||||
const existing = byKey[field.key];
|
||||
if (!existing) {
|
||||
byKey[field.key] = {
|
||||
field,
|
||||
modelIds: [modelId],
|
||||
shape
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!equal(existing.shape, shape)) {
|
||||
conflicts.push({
|
||||
key: field.key,
|
||||
modelIds: [...existing.modelIds, modelId]
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
existing.field = {
|
||||
...existing.field,
|
||||
required: existing.field.required || field.required
|
||||
};
|
||||
existing.modelIds.push(modelId);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
fields: Object.values(byKey).map((item: any) => item.field),
|
||||
conflicts
|
||||
};
|
||||
};
|
||||
|
||||
const hasValue = (value) => value !== undefined && value !== null && value !== '';
|
||||
|
||||
const getChatVariablesForm = () => {
|
||||
const { fields, conflicts } = mergeChatVariableSchemas(selectedModelIds);
|
||||
const empty =
|
||||
fields.length > 0 &&
|
||||
fields.every((field) => !hasValue(chatVariables?.[field.key]) && !hasValue(field.default));
|
||||
const missing = fields.some(
|
||||
(field) => field.required && !hasValue(chatVariables?.[field.key]) && !hasValue(field.default)
|
||||
);
|
||||
const variables = fields.reduce(
|
||||
(acc, field) => {
|
||||
const { key, ...inputField } = field;
|
||||
acc[key] = {
|
||||
...inputField,
|
||||
default: hasValue(chatVariables?.[key]) ? chatVariables[key] : inputField.default
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, any>
|
||||
);
|
||||
|
||||
return { conflicts, empty, missing, variables };
|
||||
};
|
||||
|
||||
const saveChatVariables = async (values) => {
|
||||
chatVariables = { ...chatVariables, ...values };
|
||||
|
||||
if ($chatId && !$temporaryChatEnabled && !$chatId.startsWith('local:')) {
|
||||
const res = await updateChatById(localStorage.token, $chatId, {}, chatVariables).catch(
|
||||
(err) => {
|
||||
console.error('[chat variables save]', err);
|
||||
toast.error($i18n.t('Failed to save chat variables'));
|
||||
return null;
|
||||
}
|
||||
);
|
||||
if (res) chat = res;
|
||||
}
|
||||
};
|
||||
|
||||
$: chatVariablesForm = getChatVariablesForm();
|
||||
|
||||
let oldSelectedModelIds = [''];
|
||||
$: if (!equal(selectedModelIds, oldSelectedModelIds)) {
|
||||
onSelectedModelIdsChange();
|
||||
}
|
||||
|
||||
const onSelectedModelIdsChange = () => {
|
||||
resetInput();
|
||||
oldSelectedModelIds = structuredClone(selectedModelIds);
|
||||
};
|
||||
|
||||
const mergeFiles = (current, incoming) => {
|
||||
const seen = new Set();
|
||||
return [...(incoming ?? []), ...(current ?? [])].filter((file) => {
|
||||
@@ -514,6 +619,7 @@
|
||||
currentId: null
|
||||
};
|
||||
params = {};
|
||||
chatVariables = {};
|
||||
chatFiles = [];
|
||||
files = [];
|
||||
selectedToolIds = [];
|
||||
@@ -585,16 +691,6 @@
|
||||
initiateOAuthRedirect(nextTool);
|
||||
};
|
||||
|
||||
let oldSelectedModelIds = [''];
|
||||
$: if (!equal(selectedModelIds, oldSelectedModelIds)) {
|
||||
onSelectedModelIdsChange();
|
||||
}
|
||||
|
||||
const onSelectedModelIdsChange = () => {
|
||||
resetInput();
|
||||
oldSelectedModelIds = structuredClone(selectedModelIds);
|
||||
};
|
||||
|
||||
const resetInput = async () => {
|
||||
selectedToolIds = [];
|
||||
selectedSkillIds = [];
|
||||
@@ -1703,6 +1799,7 @@
|
||||
|
||||
chatFiles = [];
|
||||
params = {};
|
||||
chatVariables = {};
|
||||
taskIds = null;
|
||||
chatTasks = [];
|
||||
|
||||
@@ -1854,6 +1951,7 @@
|
||||
noteChatDebug('getTagsById completed', { tagCount: tags?.length ?? 0 });
|
||||
|
||||
const chatContent = chat.chat;
|
||||
chatVariables = chat?.variables ?? {};
|
||||
|
||||
if (chatContent) {
|
||||
noteChatDebug('chat payload found', {
|
||||
@@ -2552,6 +2650,15 @@
|
||||
toast.error($i18n.t('Model not selected'));
|
||||
return;
|
||||
}
|
||||
if (chatVariablesForm.conflicts.length > 0) {
|
||||
showChatVariablesModal = true;
|
||||
toast.error($i18n.t('Chat Variables have conflicting model definitions'));
|
||||
return;
|
||||
}
|
||||
if (chatVariablesForm.missing || chatVariablesForm.empty) {
|
||||
showChatVariablesModal = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
files.length > 0 &&
|
||||
@@ -2968,6 +3075,8 @@
|
||||
|
||||
// Only send terminal_id if the model has terminal capability enabled
|
||||
const terminalEnabled = model.info?.meta?.capabilities?.terminal ?? true;
|
||||
const useChatVariablesFallback =
|
||||
!_chatId || $temporaryChatEnabled || _chatId.startsWith('local:');
|
||||
|
||||
const res = await generateOpenAIChatCompletion(
|
||||
localStorage.token,
|
||||
@@ -3002,6 +3111,7 @@
|
||||
$user?.email
|
||||
)
|
||||
},
|
||||
...(useChatVariablesFallback ? { chat_variables: chatVariables } : {}),
|
||||
model_item: $models.find((m) => m.id === model.id),
|
||||
|
||||
session_id: $socket?.id,
|
||||
@@ -3352,7 +3462,8 @@
|
||||
tags: [],
|
||||
timestamp: Date.now()
|
||||
},
|
||||
$selectedFolder?.id
|
||||
$selectedFolder?.id,
|
||||
chatVariables
|
||||
);
|
||||
|
||||
_chatId = chat.id;
|
||||
@@ -3515,6 +3626,47 @@
|
||||
|
||||
<audio id="audioElement" style="display: none;"></audio>
|
||||
|
||||
{#if chatVariablesForm.conflicts.length > 0}
|
||||
<Modal bind:show={showChatVariablesModal} size="md">
|
||||
<div>
|
||||
<div class="flex justify-between px-4 pt-3 pb-1 dark:text-gray-300">
|
||||
<div class="self-center text-sm font-medium">{$i18n.t('Chat Variables')}</div>
|
||||
<button
|
||||
class="self-center rounded-lg p-1 text-gray-500 transition hover:bg-gray-50 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
|
||||
on:click={() => {
|
||||
showChatVariablesModal = false;
|
||||
}}
|
||||
>
|
||||
<XMark className="size-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="px-5 pb-4 text-sm text-gray-600 dark:text-gray-300">
|
||||
<div class="mb-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
{$i18n.t('Selected models define incompatible Chat Variables.')}
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
{#each chatVariablesForm.conflicts as conflict}
|
||||
<div class="rounded-lg border border-red-200 px-3 py-2 text-xs dark:border-red-900/60">
|
||||
<div class="font-medium text-red-600 dark:text-red-400">{conflict.key}</div>
|
||||
<div class="mt-1 text-gray-500 dark:text-gray-400">
|
||||
{conflict.modelIds.join(', ')}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
{:else}
|
||||
<InputVariablesModal
|
||||
bind:show={showChatVariablesModal}
|
||||
title={$i18n.t('Chat Variables')}
|
||||
variables={chatVariablesForm.variables}
|
||||
onSave={saveChatVariables}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<WebSearchConfirmDialog
|
||||
bind:show={showWebSearchConfirm}
|
||||
title={$i18n.t('Use Web Search?')}
|
||||
@@ -3673,7 +3825,8 @@
|
||||
messages: messages,
|
||||
timestamp: Date.now()
|
||||
},
|
||||
null
|
||||
null,
|
||||
chatVariables
|
||||
);
|
||||
|
||||
if (savedChat) {
|
||||
@@ -3819,6 +3972,9 @@
|
||||
}
|
||||
}}
|
||||
onWebSearchToggle={handleWebSearchToggle}
|
||||
on:chatVariables={() => {
|
||||
showChatVariablesModal = true;
|
||||
}}
|
||||
on:submit={async (e) => {
|
||||
clearDraft($chatId);
|
||||
if (e.detail || files.length > 0) {
|
||||
@@ -3895,6 +4051,9 @@
|
||||
messageQueue={$chatRequestQueues[$chatId] ?? []}
|
||||
{chatTasks}
|
||||
onWebSearchToggle={handleWebSearchToggle}
|
||||
on:chatVariables={() => {
|
||||
showChatVariablesModal = true;
|
||||
}}
|
||||
on:submit={async (e) => {
|
||||
clearDraft($chatId);
|
||||
if (e.detail || files.length > 0) {
|
||||
@@ -3930,6 +4089,9 @@
|
||||
{onSelect}
|
||||
{onUpload}
|
||||
onWebSearchToggle={handleWebSearchToggle}
|
||||
on:chatVariables={() => {
|
||||
showChatVariablesModal = true;
|
||||
}}
|
||||
onChange={(data) => {
|
||||
if (!$temporaryChatEnabled) {
|
||||
saveDraft(data);
|
||||
|
||||
@@ -132,6 +132,11 @@
|
||||
|
||||
let selectedModelIds = [];
|
||||
$: selectedModelIds = atSelectedModel !== undefined ? [atSelectedModel.id] : selectedModels;
|
||||
$: hasChatVariables = selectedModelIds.some(
|
||||
(modelId) =>
|
||||
($models.find((model) => model.id === modelId)?.info?.meta?.chat_variables_schema?.fields
|
||||
?.length ?? 0) > 0
|
||||
);
|
||||
|
||||
export let history;
|
||||
export let taskIds = null;
|
||||
@@ -2236,6 +2241,22 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
{#if hasChatVariables}
|
||||
<Tooltip content={$i18n.t('Chat Variables')} placement="top">
|
||||
<button
|
||||
type="button"
|
||||
id="chat-variables-button"
|
||||
class="flex size-[1.875rem] shrink-0 items-center justify-center rounded-full bg-transparent text-gray-500 transition-colors hover:text-gray-800 focus:outline-hidden dark:text-gray-400 dark:hover:text-gray-100"
|
||||
aria-label={$i18n.t('Chat Variables')}
|
||||
on:click={() => {
|
||||
dispatch('chatVariables');
|
||||
}}
|
||||
>
|
||||
<Knobs className="size-4" strokeWidth="1.5" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
|
||||
{#if isActive && prompt === '' && files.length === 0}
|
||||
<div class=" flex items-center">
|
||||
<Tooltip content={$i18n.t('Stop')}>
|
||||
|
||||
@@ -237,6 +237,7 @@
|
||||
{onChange}
|
||||
{onUpload}
|
||||
{onWebSearchToggle}
|
||||
on:chatVariables
|
||||
on:submit={(e) => {
|
||||
dispatch('submit', e.detail);
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user