mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-07-29 10:43:34 +00:00
Make model presets a single global collection and treat presets as reusable setups (main, utility, embedding). Persisted scope configs now store only a preset name (Default is always present and immutable); project-scoped preset definitions were removed and attempts to edit/reset project presets are rejected. Add embedding-model support throughout: include embedding slot in presets, surface embedding in the switcher and override APIs, and notify extensions when the effective embedding model changes. Implement rename/retire propagation to update plugin configs, saved chats, and live AgentContext objects when preset names change or presets are removed. Update CLI/integration commands to use an "inherit" action for returning to the scoped preset and to report the effective scoped-or-chat preset. Add startup migration/bootstrap to initialize presets from remote or bundled fallback and numerous docs/UI text updates to reflect the new preset model. Extra: refactor config resolution helpers and add safety checks (validation, atomic writes) when saving presets.
192 lines
6.7 KiB
HTML
192 lines
6.7 KiB
HTML
<html>
|
|
<head>
|
|
<title>Edit Model Presets</title>
|
|
<script type="module">
|
|
import { store } from "/plugins/_model_config/webui/model-config-store.js";
|
|
</script>
|
|
</head>
|
|
|
|
<body>
|
|
<div x-data
|
|
x-init="
|
|
await $store.modelConfig.ensureLoaded();
|
|
$store.modelConfig.resetApiKeyDrafts();
|
|
await $store.modelConfig.refreshApiKeyStatus();
|
|
await $store.modelConfig.loadGlobalPresets();
|
|
await $store.modelConfig.preparePresetEditor($store.chats?.selected || '');
|
|
">
|
|
<template x-if="$store.modelConfig._loaded && $store.modelConfig._presetsLoaded">
|
|
<div class="preset-editor"
|
|
x-data="$store.modelConfig.createPresetEditor($store.modelConfig.presetEditorInitialName)">
|
|
<div class="preset-editor-intro">
|
|
<div class="field-title">Model Presets</div>
|
|
<div class="field-description">
|
|
Each preset contains the complete main, utility, and embedding model setup. Default is always available.
|
|
</div>
|
|
</div>
|
|
|
|
<div class="preset-editor-toolbar">
|
|
<label class="preset-editor-select">
|
|
<span class="field-title">Preset</span>
|
|
<select x-model.number="selectedKey">
|
|
<template x-for="preset in presets" :key="preset._key">
|
|
<option :value="preset._key" x-text="preset.name"></option>
|
|
</template>
|
|
</select>
|
|
</label>
|
|
<button type="button" class="button" @click="addPreset()">
|
|
<span class="icon material-symbols-outlined">add</span>
|
|
Add
|
|
</button>
|
|
<button type="button" class="button"
|
|
x-show="canDeleteSelected"
|
|
@click="$confirmClick($event, () => removeSelectedPreset())">
|
|
<span class="icon material-symbols-outlined">delete</span>
|
|
Delete
|
|
</button>
|
|
</div>
|
|
|
|
<template x-if="selectedPreset">
|
|
<div class="preset-editor-body">
|
|
<template x-if="canRenameSelected">
|
|
<div class="field preset-name-field">
|
|
<div class="field-label">
|
|
<div class="field-title">Preset name</div>
|
|
<div class="field-description">Shown in settings and the chat model switcher.</div>
|
|
</div>
|
|
<div class="field-control">
|
|
<input type="text"
|
|
x-model="selectedPreset.name"
|
|
placeholder="Preset name">
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<section class="preset-model-section">
|
|
<div class="preset-model-heading">
|
|
<div class="section-title">Main Model</div>
|
|
<div class="section-description">Primary model for conversations, reasoning, and tools.</div>
|
|
</div>
|
|
<div x-data="{ get model() { return selectedPreset.chat; }, modelType: 'chat', providers: $store.modelConfig.chatProviders, searchType: 'chat', apiKeyMode: 'store' }">
|
|
<x-component path="/plugins/_model_config/webui/model-field.html"></x-component>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="preset-model-section">
|
|
<div class="preset-model-heading">
|
|
<div class="section-title">Utility Model</div>
|
|
<div class="section-description">Faster helper model for summaries and background work.</div>
|
|
</div>
|
|
<div x-data="{ get model() { return selectedPreset.utility; }, modelType: 'utility', providers: $store.modelConfig.chatProviders, searchType: 'chat', apiKeyMode: 'store', get providerFallback() { return selectedPreset.chat.provider; }, get apiBaseFallback() { return selectedPreset.chat.api_base; } }">
|
|
<x-component path="/plugins/_model_config/webui/model-field.html"></x-component>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="preset-model-section">
|
|
<div class="preset-model-heading">
|
|
<div class="section-title">Embedding Model</div>
|
|
<div class="section-description">Creates vectors for memory and knowledge retrieval.</div>
|
|
</div>
|
|
<div x-data="{ get model() { return selectedPreset.embedding; }, modelType: 'embedding', providers: $store.modelConfig.embeddingProviders, searchType: 'embedding', apiKeyMode: 'store' }">
|
|
<x-component path="/plugins/_model_config/webui/model-field.html"></x-component>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<div class="preset-editor-secondary-actions">
|
|
<button type="button" class="text-button" @click="$store.modelConfig.openApiKeysFromSummary()">
|
|
<span class="material-symbols-outlined">key</span>
|
|
API Keys
|
|
</button>
|
|
<button type="button" class="text-button"
|
|
@click="$confirmClick($event, () => resetPresets())">
|
|
<span class="material-symbols-outlined">restart_alt</span>
|
|
Restore bundled presets
|
|
</button>
|
|
</div>
|
|
|
|
<div class="modal-footer" data-modal-footer>
|
|
<button type="button" class="btn btn-ok"
|
|
@click="if (await savePresets()) window.closeModal?.()">Save</button>
|
|
<button type="button" class="btn btn-cancel"
|
|
@click="window.closeModal?.()">Close</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<style>
|
|
.preset-editor,
|
|
.preset-editor-body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.preset-editor-intro .field-title {
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.preset-editor-toolbar {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
gap: 0.5rem;
|
|
padding: 0.8rem;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: 8px;
|
|
background: var(--color-input);
|
|
}
|
|
|
|
.preset-editor-select {
|
|
display: flex;
|
|
flex: 1 1 auto;
|
|
flex-direction: column;
|
|
gap: 0.35rem;
|
|
min-width: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
.preset-editor-select select {
|
|
width: 100%;
|
|
}
|
|
|
|
.preset-name-field,
|
|
.preset-model-section {
|
|
padding: 1rem;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.preset-model-heading {
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.preset-model-heading .section-title {
|
|
margin-top: 0;
|
|
}
|
|
|
|
.preset-editor-secondary-actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 1rem;
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
.preset-editor-toolbar {
|
|
align-items: stretch;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.preset-editor-select {
|
|
flex-basis: 100%;
|
|
}
|
|
|
|
.preset-editor-toolbar .button {
|
|
flex: 1 1 auto;
|
|
justify-content: center;
|
|
}
|
|
}
|
|
</style>
|
|
</body>
|
|
</html>
|