feat(assistant): add multi-dialog UX, task-aware llm settings, and i18n cleanup

This commit is contained in:
2026-02-23 23:45:01 +03:00
parent ab1c87ffba
commit 7df7b4f98c
30 changed files with 1145 additions and 221 deletions

View File

@@ -17,6 +17,13 @@
let providers = [];
let loading = true;
let savingPrompts = false;
let plannerProvider = '';
let plannerModel = '';
let bindings = {
dashboard_validation: '',
documentation: '',
git_commit: '',
};
let prompts = {
documentation_prompt: '',
dashboard_validation_prompt: '',
@@ -31,6 +38,30 @@
git_commit_prompt:
"Generate a concise and professional git commit message based on the following diff and recent history.\\nUse Conventional Commits format (e.g., feat: ..., fix: ..., docs: ...).\\n\\nRecent History:\\n{history}\\n\\nDiff:\\n{diff}\\n\\nCommit Message:",
};
const DEFAULT_LLM_PROVIDER_BINDINGS = {
dashboard_validation: '',
documentation: '',
git_commit: '',
};
function isMultimodalModel(modelName) {
const token = (modelName || '').toLowerCase();
if (!token) return false;
return (
token.includes('gpt-4o') ||
token.includes('gpt-4.1') ||
token.includes('vision') ||
token.includes('vl') ||
token.includes('gemini') ||
token.includes('claude-3') ||
token.includes('claude-sonnet-4')
);
}
function getProviderById(providerId) {
if (!providerId) return null;
return providers.find((item) => item.id === providerId) || null;
}
async function fetchProviders() {
loading = true;
@@ -44,6 +75,12 @@
...DEFAULT_LLM_PROMPTS,
...(consolidatedSettings?.llm?.prompts || {}),
};
bindings = {
...DEFAULT_LLM_PROVIDER_BINDINGS,
...(consolidatedSettings?.llm?.provider_bindings || {}),
};
plannerProvider = consolidatedSettings?.llm?.assistant_planner_provider || '';
plannerModel = consolidatedSettings?.llm?.assistant_planner_model || '';
} catch (err) {
console.error("Failed to fetch providers", err);
} finally {
@@ -51,7 +88,7 @@
}
}
async function savePrompts() {
async function saveSettings() {
savingPrompts = true;
try {
const current = await requestApi('/settings/consolidated');
@@ -63,12 +100,18 @@
...DEFAULT_LLM_PROMPTS,
...prompts,
},
provider_bindings: {
...DEFAULT_LLM_PROVIDER_BINDINGS,
...bindings,
},
assistant_planner_provider: plannerProvider || '',
assistant_planner_model: plannerModel || '',
},
};
await requestApi('/settings/consolidated', 'PATCH', payload);
addToast($t.settings?.save_success || 'Settings saved', 'success');
} catch (err) {
console.error('[LLMSettingsPage][Coherence:Failed] Failed to save prompts', err);
console.error('[LLMSettingsPage][Coherence:Failed] Failed to save llm settings', err);
addToast($t.settings?.save_failed || 'Failed to save settings', 'error');
} finally {
savingPrompts = false;
@@ -93,6 +136,121 @@
{:else}
<ProviderConfig {providers} onSave={fetchProviders} />
<div class="mt-6 rounded-lg border border-gray-200 bg-white p-4">
<h2 class="text-lg font-semibold text-gray-900">
{$t.settings?.llm_chatbot_settings_title || 'Chatbot Planner Settings'}
</h2>
<p class="mt-1 text-sm text-gray-600">
{$t.settings?.llm_chatbot_settings_description ||
'Select provider and optional model override for assistant intent planning.'}
</p>
<div class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2">
<div>
<label for="admin-planner-provider" class="block text-sm font-medium text-gray-700">
{$t.settings?.llm_chatbot_provider || 'Chatbot Provider'}
</label>
<select
id="admin-planner-provider"
bind:value={plannerProvider}
class="mt-1 block w-full rounded-md border border-gray-300 p-2 text-sm"
>
<option value="">{$t.dashboard?.use_default || 'Use Default'}</option>
{#each providers as provider}
<option value={provider.id}>
{provider.name} ({provider.default_model})
</option>
{/each}
</select>
</div>
<div>
<label for="admin-planner-model" class="block text-sm font-medium text-gray-700">
{$t.settings?.llm_chatbot_model || 'Chatbot Model Override'}
</label>
<input
id="admin-planner-model"
type="text"
bind:value={plannerModel}
placeholder={$t.settings?.llm_chatbot_model_placeholder || 'Optional, e.g. gpt-4.1-mini'}
class="mt-1 block w-full rounded-md border border-gray-300 p-2 text-sm"
/>
</div>
</div>
</div>
<div class="mt-6 rounded-lg border border-gray-200 bg-white p-4">
<h2 class="text-lg font-semibold text-gray-900">
{$t.settings?.llm_provider_bindings_title || 'Provider Bindings by Task'}
</h2>
<p class="mt-1 text-sm text-gray-600">
{$t.settings?.llm_provider_bindings_description ||
'Select which provider is used by default for each LLM task.'}
</p>
<div class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-2">
<div>
<label for="admin-binding-dashboard-validation" class="block text-sm font-medium text-gray-700">
{$t.settings?.llm_binding_dashboard_validation || 'Dashboard Validation Provider'}
</label>
<select
id="admin-binding-dashboard-validation"
bind:value={bindings.dashboard_validation}
class="mt-1 block w-full rounded-md border border-gray-300 p-2 text-sm"
>
<option value="">{$t.dashboard?.use_default || 'Use Default'}</option>
{#each providers as provider}
<option value={provider.id}>
{provider.name} ({provider.default_model})
</option>
{/each}
</select>
{#if bindings.dashboard_validation && !isMultimodalModel(getProviderById(bindings.dashboard_validation)?.default_model)}
<p class="mt-1 text-xs text-amber-700">
{$t.settings?.llm_multimodal_warning ||
'Dashboard validation requires a multimodal model (image input).'}
</p>
{/if}
</div>
<div>
<label for="admin-binding-documentation" class="block text-sm font-medium text-gray-700">
{$t.settings?.llm_binding_documentation || 'Documentation Provider'}
</label>
<select
id="admin-binding-documentation"
bind:value={bindings.documentation}
class="mt-1 block w-full rounded-md border border-gray-300 p-2 text-sm"
>
<option value="">{$t.dashboard?.use_default || 'Use Default'}</option>
{#each providers as provider}
<option value={provider.id}>
{provider.name} ({provider.default_model})
</option>
{/each}
</select>
</div>
<div class="md:col-span-2">
<label for="admin-binding-git-commit" class="block text-sm font-medium text-gray-700">
{$t.settings?.llm_binding_git_commit || 'Git Commit Provider'}
</label>
<select
id="admin-binding-git-commit"
bind:value={bindings.git_commit}
class="mt-1 block w-full rounded-md border border-gray-300 p-2 text-sm"
>
<option value="">{$t.dashboard?.use_default || 'Use Default'}</option>
{#each providers as provider}
<option value={provider.id}>
{provider.name} ({provider.default_model})
</option>
{/each}
</select>
</div>
</div>
</div>
<div class="mt-6 rounded-lg border border-gray-200 bg-gray-50 p-4">
<h2 class="text-lg font-semibold text-gray-900">
{$t.settings?.llm_prompts_title || 'LLM Prompt Templates'}
@@ -144,7 +302,7 @@
<button
class="rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-60"
disabled={savingPrompts}
on:click={savePrompts}
on:click={saveSettings}
>
{savingPrompts ? '...' : ($t.settings?.save_llm_prompts || 'Save LLM Prompts')}
</button>