mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-15 09:21:36 +00:00
feat(dashboard): clone profiles from any source
This commit is contained in:
parent
3380563d94
commit
28bf8fb47d
31 changed files with 182 additions and 105 deletions
|
|
@ -286,8 +286,8 @@ export const af: Translations = {
|
|||
nameRequired: "Naam word vereis",
|
||||
nameRule:
|
||||
"Slegs kleinletters, syfers, _ en -; moet met 'n letter of syfer begin; tot 64 karakters.",
|
||||
invalidName: "Ongeldige profielnaam",
|
||||
cloneFromDefault: "Kloon konfigurasie vanaf verstekprofiel",
|
||||
invalidName: "Ongeldige profielnaam", cloneFrom: "Kloon konfigurasie vanaf profiel",
|
||||
cloneFromNone: "Geen (leeg)",
|
||||
allProfiles: "Profiele",
|
||||
noProfiles: "Geen profiele gevind nie.",
|
||||
defaultBadge: "verstek",
|
||||
|
|
|
|||
|
|
@ -286,8 +286,8 @@ export const de: Translations = {
|
|||
nameRequired: "Name ist erforderlich",
|
||||
nameRule:
|
||||
"Nur Kleinbuchstaben, Ziffern, _ und -; muss mit einem Buchstaben oder einer Ziffer beginnen; maximal 64 Zeichen.",
|
||||
invalidName: "Ungültiger Profilname",
|
||||
cloneFromDefault: "Konfiguration vom Standardprofil klonen",
|
||||
invalidName: "Ungültiger Profilname", cloneFrom: "Konfiguration klonen von",
|
||||
cloneFromNone: "Keine (leer)",
|
||||
allProfiles: "Profile",
|
||||
noProfiles: "Keine Profile gefunden.",
|
||||
defaultBadge: "Standard",
|
||||
|
|
|
|||
|
|
@ -297,7 +297,8 @@ export const en: Translations = {
|
|||
nameRule:
|
||||
"Lowercase letters, digits, _ and - only; must start with a letter or digit; up to 64 characters.",
|
||||
invalidName: "Invalid profile name",
|
||||
cloneFromDefault: "Clone config from default profile",
|
||||
cloneFrom: "Clone config from",
|
||||
cloneFromNone: "None (blank)",
|
||||
allProfiles: "Profiles",
|
||||
noProfiles: "No profiles found.",
|
||||
defaultBadge: "default",
|
||||
|
|
|
|||
|
|
@ -287,7 +287,8 @@ export const es: Translations = {
|
|||
nameRule:
|
||||
"Solo letras minúsculas, dígitos, _ y -; debe comenzar con una letra o dígito; hasta 64 caracteres.",
|
||||
invalidName: "Nombre de perfil no válido",
|
||||
cloneFromDefault: "Clonar configuración del perfil predeterminado",
|
||||
cloneFrom: "Clonar desde el perfil",
|
||||
cloneFromNone: "Ninguno (vacío)",
|
||||
allProfiles: "Perfiles",
|
||||
noProfiles: "No se encontraron perfiles.",
|
||||
defaultBadge: "predeterminado",
|
||||
|
|
|
|||
|
|
@ -287,7 +287,8 @@ export const fr: Translations = {
|
|||
nameRule:
|
||||
"Lettres minuscules, chiffres, _ et - uniquement ; doit commencer par une lettre ou un chiffre ; jusqu'à 64 caractères.",
|
||||
invalidName: "Nom de profil invalide",
|
||||
cloneFromDefault: "Cloner la configuration du profil par défaut",
|
||||
cloneFrom: "Cloner depuis le profil",
|
||||
cloneFromNone: "Aucun (vide)",
|
||||
allProfiles: "Profils",
|
||||
noProfiles: "Aucun profil trouvé.",
|
||||
defaultBadge: "défaut",
|
||||
|
|
|
|||
|
|
@ -294,8 +294,8 @@ export const ga: Translations = {
|
|||
nameRequired: "Tá ainm riachtanach",
|
||||
nameRule:
|
||||
"Litreacha cás íochtair, digití, _ agus - amháin; caithfidh tús a chur le litir nó digit; suas le 64 carachtar.",
|
||||
invalidName: "Ainm próifíle neamhbhailí",
|
||||
cloneFromDefault: "Clónáil cumraíocht ón bpróifíl réamhshocraithe",
|
||||
invalidName: "Ainm próifíle neamhbhailí", cloneFrom: "Clónáil cumraíocht ón bpróifíl",
|
||||
cloneFromNone: "Dada (folamh)",
|
||||
allProfiles: "Próifílí",
|
||||
noProfiles: "Níor aimsíodh próifílí.",
|
||||
defaultBadge: "réamhshocraithe",
|
||||
|
|
|
|||
|
|
@ -286,8 +286,8 @@ export const hu: Translations = {
|
|||
nameRequired: "A név kötelező",
|
||||
nameRule:
|
||||
"Csak kisbetűk, számjegyek, _ és - karakterek; betűvel vagy számjeggyel kell kezdődnie; legfeljebb 64 karakter.",
|
||||
invalidName: "Érvénytelen profilnév",
|
||||
cloneFromDefault: "Konfiguráció klónozása az alapértelmezett profilból",
|
||||
invalidName: "Érvénytelen profilnév", cloneFrom: "Konfiguráció klónozása ebből a profilból",
|
||||
cloneFromNone: "Nincs (üres)",
|
||||
allProfiles: "Profilok",
|
||||
noProfiles: "Nem található profil.",
|
||||
defaultBadge: "alapértelmezett",
|
||||
|
|
|
|||
|
|
@ -286,8 +286,8 @@ export const it: Translations = {
|
|||
nameRequired: "Il nome è obbligatorio",
|
||||
nameRule:
|
||||
"Solo lettere minuscole, cifre, _ e -; deve iniziare con una lettera o cifra; fino a 64 caratteri.",
|
||||
invalidName: "Nome del profilo non valido",
|
||||
cloneFromDefault: "Clona la configurazione dal profilo predefinito",
|
||||
invalidName: "Nome del profilo non valido", cloneFrom: "Clona configurazione dal profilo",
|
||||
cloneFromNone: "Nessuno (vuoto)",
|
||||
allProfiles: "Profili",
|
||||
noProfiles: "Nessun profilo trovato.",
|
||||
defaultBadge: "predefinito",
|
||||
|
|
|
|||
|
|
@ -285,8 +285,8 @@ export const ja: Translations = {
|
|||
nameRequired: "名前は必須です",
|
||||
nameRule:
|
||||
"小文字、数字、_ および - のみ使用可能。最初は文字または数字で始める必要があります。最大 64 文字。",
|
||||
invalidName: "無効なプロファイル名",
|
||||
cloneFromDefault: "デフォルトプロファイルから設定を複製",
|
||||
invalidName: "無効なプロファイル名", cloneFrom: "プロファイルから複製",
|
||||
cloneFromNone: "なし(空)",
|
||||
allProfiles: "プロファイル",
|
||||
noProfiles: "プロファイルが見つかりません。",
|
||||
defaultBadge: "デフォルト",
|
||||
|
|
|
|||
|
|
@ -285,8 +285,8 @@ export const ko: Translations = {
|
|||
nameRequired: "이름은 필수입니다",
|
||||
nameRule:
|
||||
"소문자, 숫자, _ 및 - 만 사용 가능합니다. 문자나 숫자로 시작해야 하며 최대 64자입니다.",
|
||||
invalidName: "잘못된 프로필 이름입니다",
|
||||
cloneFromDefault: "기본 프로필에서 설정 복제",
|
||||
invalidName: "잘못된 프로필 이름입니다", cloneFrom: "프로필에서 복제",
|
||||
cloneFromNone: "없음 (빈 상태)",
|
||||
allProfiles: "프로필",
|
||||
noProfiles: "프로필을 찾을 수 없습니다.",
|
||||
defaultBadge: "기본",
|
||||
|
|
|
|||
|
|
@ -287,7 +287,8 @@ export const pt: Translations = {
|
|||
nameRule:
|
||||
"Apenas letras minúsculas, dígitos, _ e -; deve começar com letra ou dígito; até 64 caracteres.",
|
||||
invalidName: "Nome de perfil inválido",
|
||||
cloneFromDefault: "Clonar configuração do perfil predefinido",
|
||||
cloneFrom: "Clonar a partir do perfil",
|
||||
cloneFromNone: "Nenhum (vazio)",
|
||||
allProfiles: "Perfis",
|
||||
noProfiles: "Não foram encontrados perfis.",
|
||||
defaultBadge: "predefinido",
|
||||
|
|
|
|||
|
|
@ -286,8 +286,8 @@ export const ru: Translations = {
|
|||
nameRequired: "Имя обязательно",
|
||||
nameRule:
|
||||
"Только строчные буквы, цифры, _ и -; должно начинаться с буквы или цифры; до 64 символов.",
|
||||
invalidName: "Недопустимое имя профиля",
|
||||
cloneFromDefault: "Клонировать конфигурацию из профиля по умолчанию",
|
||||
invalidName: "Недопустимое имя профиля", cloneFrom: "Клонировать конфигурацию из профиля",
|
||||
cloneFromNone: "Нет (пусто)",
|
||||
allProfiles: "Профили",
|
||||
noProfiles: "Профили не найдены.",
|
||||
defaultBadge: "по умолчанию",
|
||||
|
|
|
|||
|
|
@ -286,8 +286,8 @@ export const tr: Translations = {
|
|||
nameRequired: "Ad gereklidir",
|
||||
nameRule:
|
||||
"Yalnızca küçük harfler, rakamlar, _ ve - kullanılabilir; harf veya rakamla başlamalı; en fazla 64 karakter.",
|
||||
invalidName: "Geçersiz profil adı",
|
||||
cloneFromDefault: "Varsayılan profilden yapılandırmayı klonla",
|
||||
invalidName: "Geçersiz profil adı", cloneFrom: "Profilden yapılandırmayı klonla",
|
||||
cloneFromNone: "Hiçbiri (boş)",
|
||||
allProfiles: "Profiller",
|
||||
noProfiles: "Profil bulunamadı.",
|
||||
defaultBadge: "varsayılan",
|
||||
|
|
|
|||
|
|
@ -354,7 +354,8 @@ export interface Translations {
|
|||
nameRequired: string;
|
||||
nameRule: string;
|
||||
invalidName: string;
|
||||
cloneFromDefault: string;
|
||||
cloneFrom: string;
|
||||
cloneFromNone: string;
|
||||
allProfiles: string;
|
||||
noProfiles: string;
|
||||
defaultBadge: string;
|
||||
|
|
|
|||
|
|
@ -287,7 +287,8 @@ export const uk: Translations = {
|
|||
nameRule:
|
||||
"Лише малі літери, цифри, _ та -; має починатися з літери або цифри; до 64 символів.",
|
||||
invalidName: "Недопустима назва профілю",
|
||||
cloneFromDefault: "Клонувати конфігурацію з профілю за замовчуванням",
|
||||
cloneFrom: "Клонувати з профілю",
|
||||
cloneFromNone: "Жоден (порожній)",
|
||||
allProfiles: "Профілі",
|
||||
noProfiles: "Профілів не знайдено.",
|
||||
defaultBadge: "за замовчуванням",
|
||||
|
|
|
|||
|
|
@ -285,8 +285,8 @@ export const zhHant: Translations = {
|
|||
nameRequired: "名稱為必填",
|
||||
nameRule:
|
||||
"僅允許小寫字母、數字、底線及連字號;首字必須為字母或數字;最多 64 個字元。",
|
||||
invalidName: "設定檔名稱無效",
|
||||
cloneFromDefault: "從預設設定檔複製設定",
|
||||
invalidName: "設定檔名稱無效", cloneFrom: "從設定檔複製",
|
||||
cloneFromNone: "無(空白)",
|
||||
allProfiles: "設定檔",
|
||||
noProfiles: "找不到設定檔。",
|
||||
defaultBadge: "預設",
|
||||
|
|
|
|||
|
|
@ -282,8 +282,8 @@ export const zh: Translations = {
|
|||
nameRequired: "名称必填",
|
||||
nameRule:
|
||||
"仅允许小写字母、数字、下划线和短横线;首字符必须是字母或数字;最多 64 个字符。",
|
||||
invalidName: "多Agent配置名称非法",
|
||||
cloneFromDefault: "从默认多Agent配置克隆配置",
|
||||
invalidName: "多Agent配置名称非法", cloneFrom: "从配置文件克隆",
|
||||
cloneFromNone: "无(空白)",
|
||||
allProfiles: "多Agent配置列表",
|
||||
noProfiles: "暂无多Agent配置。",
|
||||
defaultBadge: "默认",
|
||||
|
|
|
|||
|
|
@ -552,7 +552,8 @@ export const api = {
|
|||
}),
|
||||
createProfile: (body: {
|
||||
name: string;
|
||||
clone_from_default: boolean;
|
||||
clone_from?: string | null;
|
||||
clone_from_default?: boolean;
|
||||
clone_all?: boolean;
|
||||
no_skills?: boolean;
|
||||
description?: string;
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ export default function ProfileBuilderPage() {
|
|||
try {
|
||||
const res = await api.createProfile({
|
||||
name: n,
|
||||
clone_from_default: false,
|
||||
clone_from: null,
|
||||
description: description.trim() || undefined,
|
||||
provider: pickedModel?.provider,
|
||||
model: pickedModel?.model,
|
||||
|
|
|
|||
|
|
@ -35,11 +35,11 @@ import { Badge } from "@nous-research/ui/ui/components/badge";
|
|||
import { Button } from "@nous-research/ui/ui/components/button";
|
||||
import { Input } from "@nous-research/ui/ui/components/input";
|
||||
import { Label } from "@nous-research/ui/ui/components/label";
|
||||
import { Checkbox } from "@nous-research/ui/ui/components/checkbox";
|
||||
import {
|
||||
Select,
|
||||
SelectOption,
|
||||
} from "@nous-research/ui/ui/components/select";
|
||||
import { Checkbox } from "@nous-research/ui/ui/components/checkbox";
|
||||
import { useI18n } from "@/i18n";
|
||||
import { usePageHeader } from "@/contexts/usePageHeader";
|
||||
import { cn, themedBody } from "@/lib/utils";
|
||||
|
|
@ -312,7 +312,7 @@ export default function ProfilesPage() {
|
|||
// Create modal
|
||||
const [createModalOpen, setCreateModalOpen] = useState(false);
|
||||
const [newName, setNewName] = useState("");
|
||||
const [cloneFromDefault, setCloneFromDefault] = useState(true);
|
||||
const [cloneFrom, setCloneFrom] = useState<string | null>("default");
|
||||
const [cloneAll, setCloneAll] = useState(false);
|
||||
const [noSkills, setNoSkills] = useState(false);
|
||||
const [newDescription, setNewDescription] = useState("");
|
||||
|
|
@ -429,7 +429,7 @@ export default function ProfilesPage() {
|
|||
}
|
||||
setCreating(true);
|
||||
try {
|
||||
const cloning = cloneAll || cloneFromDefault;
|
||||
const cloning = cloneFrom !== null;
|
||||
const picked = modelChoice
|
||||
? modelChoices?.find(
|
||||
(c) => `${c.provider}\u0000${c.model}` === modelChoice,
|
||||
|
|
@ -437,8 +437,8 @@ export default function ProfilesPage() {
|
|||
: undefined;
|
||||
const res = await api.createProfile({
|
||||
name,
|
||||
clone_from_default: cloneAll ? false : cloneFromDefault,
|
||||
clone_all: cloneAll,
|
||||
clone_from: cloneFrom,
|
||||
clone_all: cloning && cloneAll,
|
||||
no_skills: cloning ? false : noSkills,
|
||||
description: newDescription.trim() || undefined,
|
||||
provider: picked?.provider,
|
||||
|
|
@ -455,7 +455,7 @@ export default function ProfilesPage() {
|
|||
setNewDescription("");
|
||||
setNoSkills(false);
|
||||
setCloneAll(false);
|
||||
setCloneFromDefault(true);
|
||||
setCloneFrom("default");
|
||||
setModelChoice("");
|
||||
setCreateModalOpen(false);
|
||||
load();
|
||||
|
|
@ -772,7 +772,7 @@ export default function ProfilesPage() {
|
|||
};
|
||||
}, [setEnd, t.common.create, loading, navigate]);
|
||||
|
||||
const cloning = cloneAll || cloneFromDefault;
|
||||
const cloning = cloneFrom !== null;
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
|
|
@ -862,6 +862,26 @@ export default function ProfilesPage() {
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="clone-from">{t.profiles.cloneFrom}</Label>
|
||||
<Select
|
||||
id="clone-from"
|
||||
value={cloneFrom ?? ""}
|
||||
onValueChange={(v) => {
|
||||
const next = v || null;
|
||||
setCloneFrom(next);
|
||||
if (next === null) setCloneAll(false);
|
||||
}}
|
||||
>
|
||||
<SelectOption value="">{t.profiles.cloneFromNone}</SelectOption>
|
||||
{profiles.map((profile) => (
|
||||
<SelectOption key={profile.name} value={profile.name}>
|
||||
{profile.name}
|
||||
</SelectOption>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor="profile-description">
|
||||
{L.descriptionOptional}
|
||||
|
|
@ -909,33 +929,19 @@ export default function ProfilesPage() {
|
|||
{L.advancedOptions}
|
||||
</legend>
|
||||
|
||||
<div className="flex items-center gap-2.5">
|
||||
<Checkbox
|
||||
checked={cloneFromDefault}
|
||||
id="clone-from-default"
|
||||
disabled={cloneAll}
|
||||
onCheckedChange={(checked) =>
|
||||
setCloneFromDefault(checked === true)
|
||||
}
|
||||
/>
|
||||
|
||||
<Label
|
||||
className="font-mondwest normal-case tracking-normal text-sm cursor-pointer"
|
||||
htmlFor="clone-from-default"
|
||||
>
|
||||
{t.profiles.cloneFromDefault}
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2.5">
|
||||
<Checkbox
|
||||
checked={cloneAll}
|
||||
disabled={!cloning}
|
||||
id="clone-all"
|
||||
onCheckedChange={(checked) => setCloneAll(checked === true)}
|
||||
/>
|
||||
|
||||
<Label
|
||||
className="font-mondwest normal-case tracking-normal text-sm cursor-pointer"
|
||||
className={cn(
|
||||
"font-mondwest normal-case tracking-normal text-sm cursor-pointer",
|
||||
!cloning && "opacity-50",
|
||||
)}
|
||||
htmlFor="clone-all"
|
||||
>
|
||||
{L.cloneAll}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue