mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-09-01 16:51:53 +03:00
refactor(models): unify preset names
This commit is contained in:
@@ -44,7 +44,6 @@ export interface AgentSettingsDraft {
|
||||
model: string;
|
||||
provider: string;
|
||||
modelPreset: string;
|
||||
presetLabel: string;
|
||||
maxTokens: number;
|
||||
contextWindowTokens: number;
|
||||
temperature: number;
|
||||
@@ -67,7 +66,6 @@ export const DEFAULT_AGENT_SETTINGS_DRAFT: AgentSettingsDraft = {
|
||||
model: "",
|
||||
provider: "",
|
||||
modelPreset: "",
|
||||
presetLabel: "",
|
||||
maxTokens: 8192,
|
||||
contextWindowTokens: 200_000,
|
||||
temperature: 0.1,
|
||||
@@ -89,7 +87,6 @@ export function agentDraftFromPayload(
|
||||
model: activePreset?.model ?? payload.agent.model,
|
||||
provider: activePreset?.provider ?? payload.agent.provider ?? payload.agent.resolved_provider ?? "",
|
||||
modelPreset: activePresetName,
|
||||
presetLabel: activePreset?.label ?? activePresetName,
|
||||
maxTokens: activePreset?.max_tokens ?? payload.agent.max_tokens,
|
||||
contextWindowTokens: normalizeContextWindowTokens(
|
||||
activePreset?.context_window_tokens ?? payload.agent.context_window_tokens,
|
||||
@@ -126,7 +123,7 @@ export function ModelPresetDeleteDialog({
|
||||
{tx(
|
||||
"settings.models.deletePresetHelp",
|
||||
"This removes the preset “{{name}}”. Provider credentials are not affected.",
|
||||
{ name: preset?.label ?? "" },
|
||||
{ name: preset?.name ?? "" },
|
||||
)}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
@@ -266,7 +263,7 @@ export function ModelsSettings({
|
||||
const modelFieldsMissing =
|
||||
!form.model.trim() ||
|
||||
!form.provider.trim() ||
|
||||
!form.presetLabel.trim() ||
|
||||
(creating && !form.modelPreset.trim()) ||
|
||||
form.maxTokens <= 0 ||
|
||||
form.temperature < 0 ||
|
||||
form.temperature > 2;
|
||||
@@ -290,7 +287,6 @@ export function ModelsSettings({
|
||||
modelPreset: preset.name,
|
||||
model: preset.model,
|
||||
provider: preset.provider,
|
||||
presetLabel: preset.label,
|
||||
maxTokens: preset.max_tokens,
|
||||
contextWindowTokens: normalizeContextWindowTokens(preset.context_window_tokens),
|
||||
temperature: preset.temperature,
|
||||
@@ -351,30 +347,29 @@ export function ModelsSettings({
|
||||
</div>
|
||||
) : null}
|
||||
<SettingsRow
|
||||
title={
|
||||
creating
|
||||
? tx("settings.models.presetName", "Preset name")
|
||||
: tx("settings.models.presetDisplayName", "Display name")
|
||||
}
|
||||
title={tx("settings.models.presetName", "Preset name")}
|
||||
description={
|
||||
creating
|
||||
? undefined
|
||||
: tx(
|
||||
"settings.models.presetDisplayNameHelp",
|
||||
"Shown in the interface. The command name stays /model {{name}}.",
|
||||
{ name: form.modelPreset },
|
||||
? tx(
|
||||
"settings.models.presetNameHelp",
|
||||
"Used everywhere, including /model commands. Names must be unique.",
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Input
|
||||
autoFocus={creating}
|
||||
value={form.presetLabel}
|
||||
placeholder={tx("settings.models.presetNamePlaceholder", "Fast writing")}
|
||||
onChange={(event) =>
|
||||
setForm((prev) => ({ ...prev, presetLabel: event.target.value }))
|
||||
}
|
||||
className="h-8 w-[min(280px,70vw)] rounded-full text-[13px]"
|
||||
/>
|
||||
{creating ? (
|
||||
<Input
|
||||
autoFocus
|
||||
value={form.modelPreset}
|
||||
placeholder={tx("settings.models.presetNamePlaceholder", "Fast writing")}
|
||||
onChange={(event) =>
|
||||
setForm((prev) => ({ ...prev, modelPreset: event.target.value }))
|
||||
}
|
||||
className="h-8 w-[min(280px,70vw)] rounded-full text-[13px]"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-[13px] font-medium text-foreground">{form.modelPreset}</span>
|
||||
)}
|
||||
</SettingsRow>
|
||||
<SettingsRow title={t("settings.rows.provider")}>
|
||||
<ProviderPicker
|
||||
@@ -604,11 +599,11 @@ export function ModelsSettings({
|
||||
draggable={ordered && !callOrderBusy}
|
||||
aria-label={
|
||||
ordered
|
||||
? `${preset?.label ?? name}. ${tx(
|
||||
? `${name}. ${tx(
|
||||
"settings.models.dragToReorder",
|
||||
"Drag to reorder",
|
||||
)}`
|
||||
: preset?.label ?? name
|
||||
: name
|
||||
}
|
||||
data-testid={`model-call-order-row-${name}`}
|
||||
onDragStart={(event) => {
|
||||
@@ -704,7 +699,7 @@ export function ModelsSettings({
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="flex min-w-0 flex-wrap items-center gap-2">
|
||||
<span className="truncate text-[14px] font-medium text-foreground">
|
||||
{preset?.label ?? name}
|
||||
{name}
|
||||
</span>
|
||||
{orderIndex === 0 ? (
|
||||
<StatusPill tone="success">
|
||||
|
||||
@@ -128,11 +128,11 @@ export function useModelSettingsActions({
|
||||
}
|
||||
|
||||
if (modelPresetCreating) {
|
||||
const label = form.presetLabel.trim();
|
||||
const name = form.modelPreset.trim();
|
||||
const provider = form.provider.trim();
|
||||
const model = form.model.trim();
|
||||
if (
|
||||
!label ||
|
||||
!name ||
|
||||
!provider ||
|
||||
!model ||
|
||||
form.maxTokens <= 0 ||
|
||||
@@ -145,7 +145,7 @@ export function useModelSettingsActions({
|
||||
setModelConfigurationSaving(true);
|
||||
try {
|
||||
const payload = await createModelConfiguration(client, {
|
||||
label,
|
||||
name,
|
||||
provider,
|
||||
model,
|
||||
maxTokens: form.maxTokens,
|
||||
@@ -190,10 +190,6 @@ export function useModelSettingsActions({
|
||||
try {
|
||||
const payload = await updateModelConfiguration(client, {
|
||||
name: selectedPreset.name,
|
||||
label:
|
||||
form.presetLabel.trim() !== selectedPreset.label
|
||||
? form.presetLabel.trim()
|
||||
: undefined,
|
||||
model: form.model !== selectedPreset.model ? form.model : undefined,
|
||||
provider: form.provider !== selectedPreset.provider ? form.provider : undefined,
|
||||
maxTokens:
|
||||
@@ -235,7 +231,6 @@ export function useModelSettingsActions({
|
||||
setForm((prev) => ({
|
||||
...prev,
|
||||
modelPreset: "",
|
||||
presetLabel: "",
|
||||
provider,
|
||||
model: "",
|
||||
maxTokens: primaryPreset?.max_tokens ?? settings.agent.max_tokens,
|
||||
|
||||
@@ -58,7 +58,7 @@ export function OverviewSettings({
|
||||
const activePresetName = settings.agent.model_preset;
|
||||
const activePreset =
|
||||
activePresetName && activePresetName !== "default"
|
||||
? settings.model_presets.find((preset) => preset.name === activePresetName)?.label ??
|
||||
? settings.model_presets.find((preset) => preset.name === activePresetName)?.name ??
|
||||
activePresetName
|
||||
: null;
|
||||
const activeProvider = settings.agent.resolved_provider ?? settings.agent.provider;
|
||||
|
||||
@@ -261,8 +261,7 @@ export function useSettingsController({
|
||||
form.maxTokens !== selectedPreset.max_tokens ||
|
||||
form.contextWindowTokens !== normalizeContextWindowTokens(selectedPreset.context_window_tokens) ||
|
||||
form.temperature !== selectedPreset.temperature ||
|
||||
form.reasoningEffort !== (selectedPreset.reasoning_effort ?? "") ||
|
||||
form.presetLabel.trim() !== selectedPreset.label
|
||||
form.reasoningEffort !== (selectedPreset.reasoning_effort ?? "")
|
||||
);
|
||||
}, [form, settings]);
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ import { cn } from "@/lib/utils";
|
||||
|
||||
export interface ModelPresetOption {
|
||||
name: string;
|
||||
label: string;
|
||||
model?: string | null;
|
||||
provider?: string | null;
|
||||
}
|
||||
@@ -99,7 +98,6 @@ export function ModelPresetBadge({
|
||||
const activePreset: ModelPresetOption = {
|
||||
...(listedIndex >= 0 ? modelPresets[listedIndex] : undefined),
|
||||
name: activeName,
|
||||
label: label || modelPresets[listedIndex]?.label || activeName,
|
||||
model: modelDetail ?? modelPresets[listedIndex]?.model,
|
||||
provider: provider || modelPresets[listedIndex]?.provider,
|
||||
};
|
||||
@@ -225,7 +223,7 @@ export function ModelPresetBadge({
|
||||
aria-valuemax={canSwitch ? presets.length - 1 : undefined}
|
||||
aria-valuemin={canSwitch ? 0 : undefined}
|
||||
aria-valuenow={canSwitch ? previewIndex : undefined}
|
||||
aria-valuetext={canSwitch ? previewPreset?.label || label : undefined}
|
||||
aria-valuetext={canSwitch ? previewPreset?.name || label : undefined}
|
||||
role={canSwitch ? "spinbutton" : undefined}
|
||||
type={interactive || canSwitch ? "button" : undefined}
|
||||
onClick={interactive ? onClick : undefined}
|
||||
@@ -290,7 +288,7 @@ export function ModelPresetBadge({
|
||||
return (
|
||||
<PresetPill
|
||||
key={virtualIndex}
|
||||
label={preset.label || preset.name}
|
||||
label={preset.name}
|
||||
modelDetail={preset.model}
|
||||
provider={preset.provider}
|
||||
isHero={isHero}
|
||||
|
||||
@@ -378,7 +378,11 @@ function toModelBadgeInfo(
|
||||
const model = scopedPreset
|
||||
? preset?.model || null
|
||||
: settings?.agent.model || modelName || null;
|
||||
const label = preset?.label?.trim() || scopedPreset || toModelBadgeLabel(model);
|
||||
const label = preset
|
||||
? preset.is_default
|
||||
? preset.label?.trim() || "Default"
|
||||
: preset.name.trim()
|
||||
: scopedPreset || toModelBadgeLabel(model);
|
||||
const rawProvider = preset?.provider
|
||||
|| (!scopedPreset ? settings?.agent.provider : null)
|
||||
|| null;
|
||||
@@ -419,7 +423,6 @@ function modelPresetOptionsFromSettings(
|
||||
const name = preset.name.trim();
|
||||
return {
|
||||
name,
|
||||
label: preset.label?.trim() || name,
|
||||
model: preset.model,
|
||||
provider: preset.resolved_provider || preset.provider,
|
||||
};
|
||||
|
||||
@@ -143,9 +143,7 @@
|
||||
"presets": "Model presets",
|
||||
"editPreset": "Edit preset",
|
||||
"presetName": "Preset name",
|
||||
"presetNameHelp": "A short name used throughout model settings.",
|
||||
"presetDisplayName": "Display name",
|
||||
"presetDisplayNameHelp": "Shown in the interface. The command name stays /model {{name}}.",
|
||||
"presetNameHelp": "Used in the interface and /model commands. Names must be unique.",
|
||||
"presetNamePlaceholder": "Fast writing",
|
||||
"advancedOptions": "Advanced options",
|
||||
"advancedSummary": "Context {{context}} · Max {{max}} tokens",
|
||||
|
||||
@@ -419,9 +419,7 @@
|
||||
"presets": "Preajustes de modelo",
|
||||
"editPreset": "Editar preajuste",
|
||||
"presetName": "Nombre del preajuste",
|
||||
"presetNameHelp": "Un nombre corto para usar en los ajustes de modelos.",
|
||||
"presetDisplayName": "Nombre para mostrar",
|
||||
"presetDisplayNameHelp": "Se muestra en la interfaz. El nombre del comando sigue siendo /model {{name}}.",
|
||||
"presetNameHelp": "Se usa en la interfaz y en /model. Los nombres deben ser únicos.",
|
||||
"presetNamePlaceholder": "Escritura rápida",
|
||||
"advancedOptions": "Opciones avanzadas",
|
||||
"advancedSummary": "Contexto {{context}} · Máx. {{max}} tokens",
|
||||
|
||||
@@ -419,9 +419,7 @@
|
||||
"presets": "Préréglages de modèle",
|
||||
"editPreset": "Modifier le préréglage",
|
||||
"presetName": "Nom du préréglage",
|
||||
"presetNameHelp": "Un nom court utilisé dans les paramètres des modèles.",
|
||||
"presetDisplayName": "Nom d’affichage",
|
||||
"presetDisplayNameHelp": "Affiché dans l’interface. Le nom de commande reste /model {{name}}.",
|
||||
"presetNameHelp": "Utilisé dans l’interface et avec /model. Les noms doivent être uniques.",
|
||||
"presetNamePlaceholder": "Rédaction rapide",
|
||||
"advancedOptions": "Options avancées",
|
||||
"advancedSummary": "Contexte {{context}} · Max. {{max}} tokens",
|
||||
|
||||
@@ -419,9 +419,7 @@
|
||||
"presets": "Prasetel model",
|
||||
"editPreset": "Ubah prasetel",
|
||||
"presetName": "Nama prasetel",
|
||||
"presetNameHelp": "Nama singkat yang digunakan di pengaturan model.",
|
||||
"presetDisplayName": "Nama tampilan",
|
||||
"presetDisplayNameHelp": "Ditampilkan di antarmuka. Nama perintah tetap /model {{name}}.",
|
||||
"presetNameHelp": "Digunakan di antarmuka dan perintah /model. Nama harus unik.",
|
||||
"presetNamePlaceholder": "Menulis cepat",
|
||||
"advancedOptions": "Opsi lanjutan",
|
||||
"advancedSummary": "Konteks {{context}} · Maks. {{max}} token",
|
||||
|
||||
@@ -419,9 +419,7 @@
|
||||
"presets": "モデルプリセット",
|
||||
"editPreset": "プリセットを編集",
|
||||
"presetName": "プリセット名",
|
||||
"presetNameHelp": "モデル設定で使用する短い名前です。",
|
||||
"presetDisplayName": "表示名",
|
||||
"presetDisplayNameHelp": "画面に表示される名前です。コマンド名は /model {{name}} のままです。",
|
||||
"presetNameHelp": "画面と /model コマンドで使用します。名前は一意である必要があります。",
|
||||
"presetNamePlaceholder": "高速執筆",
|
||||
"advancedOptions": "詳細オプション",
|
||||
"advancedSummary": "コンテキスト {{context}} · 最大 {{max}} トークン",
|
||||
|
||||
@@ -419,9 +419,7 @@
|
||||
"presets": "모델 프리셋",
|
||||
"editPreset": "프리셋 편집",
|
||||
"presetName": "프리셋 이름",
|
||||
"presetNameHelp": "모델 설정에서 사용할 짧은 이름입니다.",
|
||||
"presetDisplayName": "표시 이름",
|
||||
"presetDisplayNameHelp": "화면에 표시되는 이름입니다. 명령 이름은 /model {{name}} 그대로 유지됩니다.",
|
||||
"presetNameHelp": "화면과 /model 명령에서 사용됩니다. 이름은 고유해야 합니다.",
|
||||
"presetNamePlaceholder": "빠른 작성",
|
||||
"advancedOptions": "고급 옵션",
|
||||
"advancedSummary": "컨텍스트 {{context}} · 최대 {{max}} 토큰",
|
||||
|
||||
@@ -143,9 +143,7 @@
|
||||
"presets": "Predefinições de modelo",
|
||||
"editPreset": "Editar predefinição",
|
||||
"presetName": "Nome da predefinição",
|
||||
"presetNameHelp": "Um nome curto usado nas configurações de modelo.",
|
||||
"presetDisplayName": "Nome de exibição",
|
||||
"presetDisplayNameHelp": "Exibido na interface. O nome do comando continua sendo /model {{name}}.",
|
||||
"presetNameHelp": "Usado na interface e nos comandos /model. Os nomes devem ser únicos.",
|
||||
"presetNamePlaceholder": "Escrita rápida",
|
||||
"advancedOptions": "Opções avançadas",
|
||||
"advancedSummary": "Contexto {{context}} · Máx. {{max}} tokens",
|
||||
|
||||
@@ -419,9 +419,7 @@
|
||||
"presets": "Cấu hình mô hình đặt trước",
|
||||
"editPreset": "Sửa cấu hình đặt trước",
|
||||
"presetName": "Tên cấu hình đặt trước",
|
||||
"presetNameHelp": "Tên ngắn dùng trong phần thiết lập mô hình.",
|
||||
"presetDisplayName": "Tên hiển thị",
|
||||
"presetDisplayNameHelp": "Tên được hiển thị trong giao diện. Tên lệnh vẫn là /model {{name}}.",
|
||||
"presetNameHelp": "Dùng trong giao diện và lệnh /model. Tên phải là duy nhất.",
|
||||
"presetNamePlaceholder": "Viết nhanh",
|
||||
"advancedOptions": "Tùy chọn nâng cao",
|
||||
"advancedSummary": "Ngữ cảnh {{context}} · Tối đa {{max}} token",
|
||||
|
||||
@@ -143,9 +143,7 @@
|
||||
"presets": "模型预设",
|
||||
"editPreset": "编辑预设",
|
||||
"presetName": "预设名称",
|
||||
"presetNameHelp": "在模型设置中使用的简短名称。",
|
||||
"presetDisplayName": "显示名称",
|
||||
"presetDisplayNameHelp": "显示在界面中;命令名称仍为 /model {{name}}。",
|
||||
"presetNameHelp": "用于界面和 /model 命令;名称不能重复。",
|
||||
"presetNamePlaceholder": "快速写作",
|
||||
"advancedOptions": "高级选项",
|
||||
"advancedSummary": "上下文 {{context}} · 最大输出 {{max}} tokens",
|
||||
|
||||
@@ -419,9 +419,7 @@
|
||||
"presets": "模型預設",
|
||||
"editPreset": "編輯預設",
|
||||
"presetName": "預設名稱",
|
||||
"presetNameHelp": "模型設定中使用的簡短名稱。",
|
||||
"presetDisplayName": "顯示名稱",
|
||||
"presetDisplayNameHelp": "顯示在介面中;命令名稱仍為 /model {{name}}。",
|
||||
"presetNameHelp": "用於介面和 /model 命令;名稱不得重複。",
|
||||
"presetNamePlaceholder": "快速寫作",
|
||||
"advancedOptions": "進階選項",
|
||||
"advancedSummary": "上下文 {{context}} · 最大輸出 {{max}} tokens",
|
||||
|
||||
@@ -910,8 +910,7 @@ export async function createModelConfiguration(
|
||||
transport,
|
||||
"settings.model_configuration.create",
|
||||
{
|
||||
...(configuration.name !== undefined ? { name: configuration.name } : {}),
|
||||
label: configuration.label,
|
||||
name: configuration.name,
|
||||
provider: configuration.provider,
|
||||
model: configuration.model,
|
||||
...modelGenerationSettingsPayload(configuration),
|
||||
@@ -928,7 +927,6 @@ export async function updateModelConfiguration(
|
||||
"settings.model_configuration.update",
|
||||
{
|
||||
name: configuration.name,
|
||||
...(configuration.label !== undefined ? { label: configuration.label } : {}),
|
||||
...(configuration.provider !== undefined ? { provider: configuration.provider } : {}),
|
||||
...(configuration.model !== undefined ? { model: configuration.model } : {}),
|
||||
...modelGenerationSettingsPayload(configuration),
|
||||
|
||||
@@ -522,7 +522,8 @@ export interface SettingsPayload {
|
||||
};
|
||||
model_presets: Array<{
|
||||
name: string;
|
||||
label: string;
|
||||
/** @deprecated Compatibility alias. New clients must use `name`. */
|
||||
label?: string;
|
||||
active: boolean;
|
||||
is_default: boolean;
|
||||
model: string;
|
||||
@@ -1085,8 +1086,7 @@ export interface SettingsUpdate {
|
||||
}
|
||||
|
||||
export interface ModelConfigurationCreate {
|
||||
name?: string;
|
||||
label: string;
|
||||
name: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
maxTokens?: number;
|
||||
@@ -1097,7 +1097,6 @@ export interface ModelConfigurationCreate {
|
||||
|
||||
export interface ModelConfigurationUpdate {
|
||||
name: string;
|
||||
label?: string;
|
||||
provider?: string;
|
||||
model?: string;
|
||||
maxTokens?: number;
|
||||
|
||||
@@ -439,7 +439,7 @@ describe("webui API helpers", () => {
|
||||
|
||||
it("serializes model configuration creation", async () => {
|
||||
await createModelConfiguration(mutationTransport, {
|
||||
label: "Fast writing",
|
||||
name: "Fast writing",
|
||||
provider: "openai",
|
||||
model: "openai/gpt-4.1-mini",
|
||||
maxTokens: 4096,
|
||||
@@ -451,7 +451,7 @@ describe("webui API helpers", () => {
|
||||
expect(requestMutation).toHaveBeenCalledWith(
|
||||
"settings.model_configuration.create",
|
||||
{
|
||||
label: "Fast writing",
|
||||
name: "Fast writing",
|
||||
provider: "openai",
|
||||
model: "openai/gpt-4.1-mini",
|
||||
max_tokens: 4096,
|
||||
@@ -466,7 +466,6 @@ describe("webui API helpers", () => {
|
||||
it("serializes model configuration updates", async () => {
|
||||
await updateModelConfiguration(mutationTransport, {
|
||||
name: "codex",
|
||||
label: "Codex",
|
||||
provider: "openai_codex",
|
||||
model: "openai-codex/gpt-5.5",
|
||||
maxTokens: 8192,
|
||||
@@ -479,7 +478,6 @@ describe("webui API helpers", () => {
|
||||
"settings.model_configuration.update",
|
||||
{
|
||||
name: "codex",
|
||||
label: "Codex",
|
||||
provider: "openai_codex",
|
||||
model: "openai-codex/gpt-5.5",
|
||||
max_tokens: 8192,
|
||||
|
||||
@@ -89,7 +89,7 @@ async function togglePresetEditor(name = "primary") {
|
||||
describe("Settings models", () => {
|
||||
installSettingsViewTestHooks();
|
||||
|
||||
it("distinguishes the editable display name from the stable command name", async () => {
|
||||
it("uses the preset name as the canonical identity", async () => {
|
||||
const payload = settingsPayload();
|
||||
payload.model_presets[0] = {
|
||||
...payload.model_presets[0],
|
||||
@@ -104,11 +104,10 @@ describe("Settings models", () => {
|
||||
|
||||
await togglePresetEditor("openai");
|
||||
|
||||
expect(screen.getByText("Display name")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText("Shown in the interface. The command name stays /model openai."),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue("minimax")).toBeInTheDocument();
|
||||
const editor = screen.getByTestId("model-preset-editor");
|
||||
expect(within(editor).getByText("Preset name")).toBeInTheDocument();
|
||||
expect(within(editor).getByText("openai")).toBeInTheDocument();
|
||||
expect(within(editor).queryByText("minimax")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps generation parameters collapsed until advanced options are opened", async () => {
|
||||
@@ -188,7 +187,7 @@ describe("Settings models", () => {
|
||||
"lg:max-w-6xl",
|
||||
"rounded-floating",
|
||||
);
|
||||
expect(within(editor).getByDisplayValue("Primary")).toBeInTheDocument();
|
||||
expect(within(editor).getByText("primary")).toBeInTheDocument();
|
||||
const deleteButton = within(editor).getByRole("button", { name: "Delete" });
|
||||
expect(deleteButton).toBeDisabled();
|
||||
expect(deleteButton).toHaveAttribute("aria-describedby", "model-preset-delete-hint");
|
||||
@@ -244,8 +243,9 @@ describe("Settings models", () => {
|
||||
),
|
||||
);
|
||||
await togglePresetEditor();
|
||||
fireEvent.change(screen.getByDisplayValue("Primary"), {
|
||||
target: { value: "Primary draft" },
|
||||
fireEvent.click(screen.getByRole("button", { name: /Advanced options/ }));
|
||||
fireEvent.change(screen.getByLabelText("Temperature"), {
|
||||
target: { value: "0.4" },
|
||||
});
|
||||
const primaryRow = screen.getByTestId("model-call-order-row-primary");
|
||||
const backupRow = screen.getByTestId("model-call-order-row-backup");
|
||||
@@ -270,7 +270,7 @@ describe("Settings models", () => {
|
||||
});
|
||||
|
||||
expect(screen.queryByRole("button", { name: "Save order" })).not.toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue("Primary draft")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Temperature")).toHaveValue(0.4);
|
||||
expect(screen.getByRole("button", { name: "Save preset" })).toBeEnabled();
|
||||
});
|
||||
|
||||
@@ -430,7 +430,8 @@ describe("Settings models", () => {
|
||||
renderSettingsView({ initialSection: "models", initialSettings: payloadWithCodex });
|
||||
|
||||
const codexRow = await screen.findByTestId("model-call-order-row-codex");
|
||||
expect(codexRow).toHaveTextContent("Codex");
|
||||
expect(codexRow).toHaveTextContent("codex");
|
||||
expect(codexRow).not.toHaveTextContent("Codex");
|
||||
expect(codexRow).toHaveTextContent("openai-codex/gpt-5.5");
|
||||
expect(codexRow).toHaveTextContent("Disabled");
|
||||
expect(codexRow).toHaveAttribute("draggable", "false");
|
||||
@@ -461,7 +462,7 @@ describe("Settings models", () => {
|
||||
const { payload } = settingsPayloadWithBackup();
|
||||
const writerPreset = {
|
||||
...payload.model_presets[0],
|
||||
name: "writer",
|
||||
name: "Writer",
|
||||
label: "Writer",
|
||||
active: false,
|
||||
model: "openai/gpt-4o-mini",
|
||||
@@ -475,7 +476,7 @@ describe("Settings models", () => {
|
||||
};
|
||||
const orderedPayload: SettingsPayload = {
|
||||
...createdPayload,
|
||||
model_call_order: ["primary", "backup", "writer"],
|
||||
model_call_order: ["primary", "backup", "Writer"],
|
||||
created_model_preset: undefined,
|
||||
};
|
||||
const fetchMock = vi.fn(async (input: RequestInfo | URL) => {
|
||||
@@ -528,15 +529,15 @@ describe("Settings models", () => {
|
||||
await waitFor(() => {
|
||||
expect(requestMutationMock).toHaveBeenLastCalledWith(
|
||||
"settings.model_call_order.update",
|
||||
{ order: ["primary", "backup", "writer"] },
|
||||
{ order: ["primary", "backup", "Writer"] },
|
||||
20_000,
|
||||
);
|
||||
});
|
||||
const writerRow = await screen.findByTestId("model-call-order-row-writer");
|
||||
const writerRow = await screen.findByTestId("model-call-order-row-Writer");
|
||||
expect(writerRow).not.toHaveTextContent("Disabled");
|
||||
expect(writerRow).not.toHaveTextContent(/Fallback/);
|
||||
expect(within(writerRow).getByRole("switch", { name: "Disable preset" })).toBeChecked();
|
||||
expect(screen.getByDisplayValue("Writer")).toBeInTheDocument();
|
||||
expect(screen.getAllByText("Writer").length).toBeGreaterThan(0);
|
||||
expect(screen.queryByRole("button", { name: "Save order" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
@@ -307,9 +307,9 @@ function ascii(bytes: Uint8Array, offset: number, length: number): string {
|
||||
}
|
||||
|
||||
const MODEL_PRESETS = [
|
||||
{ name: "kimi", label: "Kimi", provider: "moonshot" },
|
||||
{ name: "dflash", label: "DFlash", provider: "deepseek" },
|
||||
{ name: "dspro", label: "DS Pro", provider: "deepseek" },
|
||||
{ name: "kimi", provider: "moonshot" },
|
||||
{ name: "dflash", provider: "deepseek" },
|
||||
{ name: "dspro", provider: "deepseek" },
|
||||
];
|
||||
|
||||
function renderPresetComposer(variant: "thread" | "hero" = "thread") {
|
||||
@@ -317,7 +317,7 @@ function renderPresetComposer(variant: "thread" | "hero" = "thread") {
|
||||
render(
|
||||
<ThreadComposer
|
||||
onSend={vi.fn()}
|
||||
modelLabel="Kimi"
|
||||
modelLabel="kimi"
|
||||
modelPreset="kimi"
|
||||
modelProvider="moonshot"
|
||||
modelPresets={MODEL_PRESETS}
|
||||
@@ -327,7 +327,7 @@ function renderPresetComposer(variant: "thread" | "hero" = "thread") {
|
||||
/>,
|
||||
);
|
||||
return {
|
||||
badge: screen.getByRole("spinbutton", { name: "Kimi" }),
|
||||
badge: screen.getByRole("spinbutton", { name: "kimi" }),
|
||||
onPresetChange,
|
||||
};
|
||||
}
|
||||
@@ -604,7 +604,7 @@ describe("ThreadComposer", () => {
|
||||
expect(Array.from(pills).every((pill) => pill.querySelector("img"))).toBe(true);
|
||||
expect(Array.from(badge.querySelectorAll("img")).every((image) => !image.draggable)).toBe(true);
|
||||
const centeredPill = track.querySelector<HTMLElement>("[data-preset-offset='0']");
|
||||
expect(centeredPill).toHaveTextContent("Kimi");
|
||||
expect(centeredPill).toHaveTextContent("kimi");
|
||||
expect(centeredPill).toHaveStyle({ transform: "scale(1.0800)" });
|
||||
expect(
|
||||
track.querySelector<HTMLElement>("[data-preset-offset='1']"),
|
||||
@@ -615,13 +615,13 @@ describe("ThreadComposer", () => {
|
||||
pointerId: 7,
|
||||
pointerType: "mouse",
|
||||
});
|
||||
expect(track.querySelector("[data-preset-offset='0']")).toHaveTextContent("Kimi");
|
||||
expect(track.querySelector("[data-preset-offset='0']")).toHaveTextContent("kimi");
|
||||
fireEvent.pointerMove(badge, {
|
||||
clientY: 123,
|
||||
pointerId: 7,
|
||||
pointerType: "mouse",
|
||||
});
|
||||
expect(track.querySelector("[data-preset-offset='0']")).toHaveTextContent("DS Pro");
|
||||
expect(track.querySelector("[data-preset-offset='0']")).toHaveTextContent("dspro");
|
||||
fireEvent.pointerUp(badge, {
|
||||
clientY: 123,
|
||||
pointerId: 7,
|
||||
|
||||
@@ -609,7 +609,7 @@ describe("ThreadShell", () => {
|
||||
),
|
||||
);
|
||||
|
||||
expect(await screen.findByTitle("Fast · gpt-5.5 · OpenAI Codex")).toBeInTheDocument();
|
||||
expect(await screen.findByTitle("fast · gpt-5.5 · OpenAI Codex")).toBeInTheDocument();
|
||||
expect(screen.queryByTitle("Default · deepseek-v4-pro · DeepSeek")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -645,19 +645,19 @@ describe("ThreadShell", () => {
|
||||
"preset-order",
|
||||
"/model fast",
|
||||
);
|
||||
expect(await screen.findByText("Fast")).toBeInTheDocument();
|
||||
expect(await screen.findByText("fast")).toBeInTheDocument();
|
||||
fireEvent.keyDown(
|
||||
screen.getByRole("spinbutton", { name: "Fast" }),
|
||||
screen.getByRole("spinbutton", { name: "fast" }),
|
||||
{ key: "End" },
|
||||
);
|
||||
expect(client.sendSystemCommand).toHaveBeenLastCalledWith(
|
||||
"preset-order",
|
||||
"/model extra",
|
||||
);
|
||||
expect(await screen.findByText("Extra")).toBeInTheDocument();
|
||||
expect(await screen.findByText("extra")).toBeInTheDocument();
|
||||
|
||||
rerender(view("fast"));
|
||||
expect(await screen.findByText("Fast")).toBeInTheDocument();
|
||||
expect(await screen.findByText("fast")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("uses the backend-resolved provider for an auto session preset", async () => {
|
||||
@@ -692,7 +692,7 @@ describe("ThreadShell", () => {
|
||||
),
|
||||
);
|
||||
|
||||
expect(await screen.findByTitle("Fast · gpt-4 · Company Proxy")).toBeInTheDocument();
|
||||
expect(await screen.findByTitle("fast · gpt-4 · Company Proxy")).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Model not configured" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -1070,7 +1070,7 @@ describe("ThreadShell", () => {
|
||||
await screen.findByRole("spinbutton", { name: "Default" }),
|
||||
{ key: "ArrowDown" },
|
||||
);
|
||||
expect(await screen.findByText("Fast")).toBeInTheDocument();
|
||||
expect(await screen.findByText("fast")).toBeInTheDocument();
|
||||
expect(client.sendSystemCommand).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.change(screen.getByLabelText("Message input"), {
|
||||
|
||||
Reference in New Issue
Block a user