This commit is contained in:
decolua committed 2026-06-15 18:18:04 +07:00
1 parent 8ab5af0052
commit b282f05549
66 files changed
+2328 -213

No files matched your search

+17 -6
View File
@@ -5,7 +5,7 @@ import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, us
import { arrayMove, SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { restrictToVerticalAxis, restrictToParentElement } from "@dnd-kit/modifiers";
import { Card, Button, Modal, Input, CardSkeleton, ModelSelectModal, Toggle, ConfirmModal } from "@/shared/components";
import { Card, Button, Modal, Input, CardSkeleton, ModelSelectModal, Toggle, ConfirmModal, CapacityBadges } from "@/shared/components";
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers";
@@ -19,6 +19,7 @@ export default function CombosPage() {
const [editingCombo, setEditingCombo] = useState(null);
const [activeProviders, setActiveProviders] = useState([]);
const [comboStrategies, setComboStrategies] = useState({});
const [modelCaps, setModelCaps] = useState({});
const [confirmState, setConfirmState] = useState(null);
const { copied, copy } = useCopyToClipboard();
@@ -28,10 +29,11 @@ export default function CombosPage() {
const fetchData = async () => {
try {
const [combosRes, providersRes, settingsRes] = await Promise.all([
const [combosRes, providersRes, settingsRes, modelsRes] = await Promise.all([
fetch("/api/combos"),
fetch("/api/providers"),
fetch("/api/settings"),
fetch("/api/models"),
]);
const combosData = await combosRes.json();
const providersData = await providersRes.json();
@@ -42,6 +44,13 @@ export default function CombosPage() {
if (providersRes.ok) {
setActiveProviders(providersData.connections || []);
}
if (modelsRes.ok) {
const md = await modelsRes.json();
// Build fullModel -> caps map for badge lookup
const map = {};
for (const m of md.models || []) if (m.caps) map[m.fullModel] = m.caps;
setModelCaps(map);
}
setComboStrategies(settingsData.comboStrategies || {});
} catch (error) {
console.log("Error fetching data:", error);
@@ -143,7 +152,7 @@ export default function CombosPage() {
<div className="min-w-0">
<h1 className="text-2xl font-semibold">Combos</h1>
<p className="text-sm text-text-muted mt-1">
Create model combos with fallback support
Create model combos with fallback support — auto-adapts per request: routes images to vision models and web search to search-capable models.
</p>
</div>
<Button icon="add" onClick={() => setShowCreateModal(true)} className="w-full sm:w-auto">
@@ -171,6 +180,7 @@ export default function CombosPage() {
<ComboCard
key={combo.id}
combo={combo}
modelCaps={modelCaps}
copied={copied}
onCopy={copy}
onEdit={() => setEditingCombo(combo)}
@@ -214,7 +224,7 @@ export default function CombosPage() {
);
}
function ComboCard({ combo, copied, onCopy, onEdit, onDelete, roundRobinEnabled, onToggleRoundRobin }) {
function ComboCard({ combo, modelCaps = {}, copied, onCopy, onEdit, onDelete, roundRobinEnabled, onToggleRoundRobin }) {
return (
<Card padding="sm" className="group">
<div className="flex min-w-0 flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
@@ -229,8 +239,9 @@ function ComboCard({ combo, copied, onCopy, onEdit, onDelete, roundRobinEnabled,
<span className="text-xs text-text-muted italic">No models</span>
) : (
combo.models.slice(0, 3).map((model, index) => (
<code key={index} className="max-w-full truncate rounded bg-black/5 px-1.5 py-0.5 font-mono text-[10px] text-text-muted dark:bg-white/5 sm:max-w-[220px]">
{model}
<code key={index} className="inline-flex items-center gap-1 rounded bg-black/5 px-1.5 py-0.5 font-mono text-xs text-text-muted dark:bg-white/5">
<span>{model}</span>
<CapacityBadges caps={modelCaps[model]} />
</code>
))
)}