fix(model): require confirmation for expensive model selections
Rebased onto current main and re-ported across the restructured surfaces: model flows now thread confirm_provider/base_url/api_key through hermes_cli/model_setup_flows.py, the Discord picker lives in plugins/platforms/discord/adapter.py, and the web dashboard picker applies chat-mode switches via config.set so the expensive-model confirmation can ride the response. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
committed by
Teknium
co-authored by
Claude Fable 5
parent
4eadef18a9
commit
af978ecb17
@@ -26,7 +26,7 @@ import { Spinner } from "@nous-research/ui/ui/components/spinner";
|
||||
import { Stats } from "@nous-research/ui/ui/components/stats";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@nous-research/ui/ui/components/card";
|
||||
import { Badge } from "@nous-research/ui/ui/components/badge";
|
||||
import { ConfirmDialog } from "@nous-research/ui/ui/components/confirm-dialog";
|
||||
import { ConfirmDialog } from "@/components/ConfirmDialog";
|
||||
import { useModalBehavior } from "@/hooks/useModalBehavior";
|
||||
import { usePageHeader } from "@/contexts/usePageHeader";
|
||||
import { useI18n } from "@/i18n";
|
||||
@@ -209,10 +209,16 @@ function UseAsMenu({
|
||||
const [open, setOpen] = useState(false);
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [pendingConfirm, setPendingConfirm] = useState<{
|
||||
message: string;
|
||||
scope: "main" | "auxiliary";
|
||||
task: string;
|
||||
} | null>(null);
|
||||
|
||||
const assign = async (
|
||||
scope: "main" | "auxiliary",
|
||||
task: string,
|
||||
confirmExpensiveModel = false,
|
||||
) => {
|
||||
if (!provider || !model) {
|
||||
setError("Missing provider/model");
|
||||
@@ -221,7 +227,23 @@ function UseAsMenu({
|
||||
setBusy(true);
|
||||
setError(null);
|
||||
try {
|
||||
await api.setModelAssignment({ scope, provider, model, task });
|
||||
const result = await api.setModelAssignment({
|
||||
confirm_expensive_model: confirmExpensiveModel,
|
||||
scope,
|
||||
provider,
|
||||
model,
|
||||
task,
|
||||
});
|
||||
if (result.confirm_required) {
|
||||
setPendingConfirm({
|
||||
scope,
|
||||
task,
|
||||
message:
|
||||
result.confirm_message ||
|
||||
"This model has unusually high known pricing.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
onAssigned();
|
||||
setOpen(false);
|
||||
} catch (e) {
|
||||
@@ -310,6 +332,22 @@ function UseAsMenu({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<ConfirmDialog
|
||||
open={!!pendingConfirm}
|
||||
title="Expensive Model Warning"
|
||||
description={pendingConfirm?.message}
|
||||
destructive
|
||||
confirmLabel="Switch anyway"
|
||||
cancelLabel="Cancel"
|
||||
loading={busy}
|
||||
onCancel={() => setPendingConfirm(null)}
|
||||
onConfirm={() => {
|
||||
const pending = pendingConfirm;
|
||||
if (!pending) return;
|
||||
setPendingConfirm(null);
|
||||
void assign(pending.scope, pending.task, true);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -619,14 +657,16 @@ function AuxiliaryTasksModal({
|
||||
AUX_TASKS.find((t) => t.key === picker.task)?.label ??
|
||||
picker.task
|
||||
}`}
|
||||
onApply={async ({ provider, model }) => {
|
||||
await api.setModelAssignment({
|
||||
onApply={async ({ provider, model, confirmExpensiveModel }) => {
|
||||
const result = await api.setModelAssignment({
|
||||
confirm_expensive_model: confirmExpensiveModel,
|
||||
scope: "auxiliary",
|
||||
task: picker.task,
|
||||
provider,
|
||||
model,
|
||||
});
|
||||
onSaved();
|
||||
if (!result.confirm_required) onSaved();
|
||||
return result;
|
||||
}}
|
||||
onClose={() => setPicker(null)}
|
||||
/>
|
||||
@@ -666,14 +706,23 @@ function ModelSettingsPanel({
|
||||
task,
|
||||
provider,
|
||||
model,
|
||||
confirmExpensiveModel,
|
||||
}: {
|
||||
confirmExpensiveModel?: boolean;
|
||||
scope: "main" | "auxiliary";
|
||||
task: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
}) => {
|
||||
await api.setModelAssignment({ scope, task, provider, model });
|
||||
onSaved();
|
||||
const result = await api.setModelAssignment({
|
||||
confirm_expensive_model: confirmExpensiveModel,
|
||||
scope,
|
||||
task,
|
||||
provider,
|
||||
model,
|
||||
});
|
||||
if (!result.confirm_required) onSaved();
|
||||
return result;
|
||||
};
|
||||
|
||||
// Count how many aux tasks have overrides
|
||||
@@ -749,14 +798,15 @@ function ModelSettingsPanel({
|
||||
loader={api.getModelOptions}
|
||||
alwaysGlobal
|
||||
title="Set Main Model"
|
||||
onApply={async ({ provider, model }) => {
|
||||
await applyAssignment({
|
||||
onApply={({ provider, model, confirmExpensiveModel }) =>
|
||||
applyAssignment({
|
||||
confirmExpensiveModel,
|
||||
scope: "main",
|
||||
task: "",
|
||||
provider,
|
||||
model,
|
||||
});
|
||||
}}
|
||||
})
|
||||
}
|
||||
onClose={() => setPicker(null)}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user