fix(combos): show effective strategy vs global default; keep explicit fallback override
This commit is contained in:
@@ -51,6 +51,7 @@ export default function CombosPage() {
|
||||
const [editingCombo, setEditingCombo] = useState(null);
|
||||
const [activeProviders, setActiveProviders] = useState([]);
|
||||
const [comboStrategies, setComboStrategies] = useState({});
|
||||
const [globalComboStrategy, setGlobalComboStrategy] = useState("fallback");
|
||||
const [capacityAdapter, setCapacityAdapter] = useState(EMPTY_CAPACITY_ADAPTER);
|
||||
const { getCaps } = useModelCaps();
|
||||
const [confirmState, setConfirmState] = useState(null);
|
||||
@@ -77,6 +78,7 @@ export default function CombosPage() {
|
||||
setActiveProviders(providersData.connections || []);
|
||||
}
|
||||
setComboStrategies(settingsData.comboStrategies || {});
|
||||
setGlobalComboStrategy(settingsData.comboStrategy || "fallback");
|
||||
const rawAdapter = settingsData.capacityAdapter || {};
|
||||
const normalized = {};
|
||||
for (const cap of CAPACITY_ADAPTER_CAPS) {
|
||||
@@ -159,14 +161,19 @@ export default function CombosPage() {
|
||||
});
|
||||
};
|
||||
|
||||
// Merge a per-combo strategy patch into settings.comboStrategies. Passing an empty
|
||||
// patch (strategy back to default "fallback") drops the entry entirely.
|
||||
// Merge a per-combo strategy patch into settings.comboStrategies.
|
||||
// A "fallback" entry is only pruned when the global strategy is also fallback;
|
||||
// otherwise it's kept so the combo explicitly overrides global round-robin/fusion.
|
||||
const handleSetComboStrategy = async (comboName, patch) => {
|
||||
try {
|
||||
const updated = { ...comboStrategies };
|
||||
const next = { ...(updated[comboName] || {}), ...patch };
|
||||
// Prune to keep settings clean: default fallback with no extras = no entry.
|
||||
if (!next.fallbackStrategy || next.fallbackStrategy === "fallback") {
|
||||
// Prune to keep settings clean: default fallback with no extras = no entry,
|
||||
// but only when it matches the global default (otherwise it's an override).
|
||||
if (
|
||||
(!next.fallbackStrategy || next.fallbackStrategy === "fallback") &&
|
||||
(globalComboStrategy === "fallback" || !globalComboStrategy)
|
||||
) {
|
||||
delete updated[comboName];
|
||||
} else {
|
||||
updated[comboName] = next;
|
||||
@@ -239,6 +246,7 @@ export default function CombosPage() {
|
||||
onEdit={() => setEditingCombo(combo)}
|
||||
onDelete={() => handleDelete(combo.id)}
|
||||
strategy={comboStrategies[combo.name] || {}}
|
||||
globalStrategy={globalComboStrategy}
|
||||
onSetStrategy={(patch) => handleSetComboStrategy(combo.name, patch)}
|
||||
/>
|
||||
))}
|
||||
@@ -294,9 +302,11 @@ const STRATEGY_OPTIONS = [
|
||||
{ value: "fusion", label: "Fusion — panel + judge" },
|
||||
];
|
||||
|
||||
function ComboCard({ combo, getCaps, activeProviders = [], copied, onCopy, onEdit, onDelete, strategy = {}, onSetStrategy }) {
|
||||
function ComboCard({ combo, getCaps, activeProviders = [], copied, onCopy, onEdit, onDelete, strategy = {}, globalStrategy = "fallback", onSetStrategy }) {
|
||||
const [showJudgeSelect, setShowJudgeSelect] = useState(false);
|
||||
const current = strategy.fallbackStrategy || "fallback";
|
||||
// Show the effective strategy: per-combo override first, then the global
|
||||
// default (combos without an entry fall through to settings.comboStrategy).
|
||||
const current = strategy.fallbackStrategy || globalStrategy || "fallback";
|
||||
const judge = strategy.judgeModel || "";
|
||||
const isFusion = current === "fusion";
|
||||
|
||||
|
||||
@@ -84,7 +84,9 @@ export default function ComboDetailPage() {
|
||||
setName(c.name);
|
||||
setProviders(c.models || []);
|
||||
const s = settingsRes.ok ? await settingsRes.json() : {};
|
||||
setRoundRobin(s.comboStrategies?.[c.name]?.fallbackStrategy === "round-robin");
|
||||
// Effective strategy: per-combo override first, then the global default.
|
||||
const eff = s.comboStrategies?.[c.name]?.fallbackStrategy || s.comboStrategy || "fallback";
|
||||
setRoundRobin(eff === "round-robin");
|
||||
const allLogs = logsRes.ok ? await logsRes.json() : [];
|
||||
setLogs(allLogs.filter((l) => typeof l === "string" && l.includes(c.name)).slice(0, 50));
|
||||
} catch { /* noop */ }
|
||||
@@ -154,8 +156,14 @@ export default function ComboDetailPage() {
|
||||
const settingsRes = await fetch("/api/settings", { cache: "no-store" });
|
||||
const s = settingsRes.ok ? await settingsRes.json() : {};
|
||||
const updated = { ...(s.comboStrategies || {}) };
|
||||
if (enabled) updated[combo.name] = { fallbackStrategy: "round-robin" };
|
||||
else delete updated[combo.name];
|
||||
if (enabled) {
|
||||
updated[combo.name] = { fallbackStrategy: "round-robin" };
|
||||
} else {
|
||||
// Explicit fallback override so the combo stays ordered even when the
|
||||
// global default is round-robin (a bare delete would inherit it).
|
||||
if ((s.comboStrategy || "fallback") === "fallback") delete updated[combo.name];
|
||||
else updated[combo.name] = { fallbackStrategy: "fallback" };
|
||||
}
|
||||
await fetch("/api/settings", {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
||||
Reference in New Issue
Block a user