diff --git a/open-sse/services/combo.ts b/open-sse/services/combo.ts
index 69f7ea86ad..46dc3953f3 100644
--- a/open-sse/services/combo.ts
+++ b/open-sse/services/combo.ts
@@ -310,7 +310,8 @@ export async function handleComboChat({
const parsed = parseModel(modelStr);
const provider = parsed.provider || parsed.providerAlias || "unknown";
const profile = getProviderProfile(provider);
- const breaker = getCircuitBreaker(`combo:${provider}`, {
+ const breakerKey = `combo:${modelStr}`;
+ const breaker = getCircuitBreaker(breakerKey, {
failureThreshold: profile.circuitBreakerThreshold,
resetTimeout: profile.circuitBreakerReset,
});
@@ -440,8 +441,7 @@ export async function handleComboChat({
// Early exit: check if all models have breaker OPEN
const allBreakersOpen = orderedModels.every((m) => {
- const p = parseModel(m).provider || parseModel(m).providerAlias || "unknown";
- return !getCircuitBreaker(`combo:${p}`).canExecute();
+ return !getCircuitBreaker(`combo:${m}`).canExecute();
});
// All models failed
@@ -532,7 +532,8 @@ async function handleRoundRobinCombo({
const parsed = parseModel(modelStr);
const provider = parsed.provider || parsed.providerAlias || "unknown";
const profile = getProviderProfile(provider);
- const breaker = getCircuitBreaker(`combo:${provider}`, {
+ const breakerKey = `combo:${modelStr}`;
+ const breaker = getCircuitBreaker(breakerKey, {
failureThreshold: profile.circuitBreakerThreshold,
resetTimeout: profile.circuitBreakerReset,
});
@@ -694,8 +695,7 @@ async function handleRoundRobinCombo({
// Early exit: check if all models have breaker OPEN
const allBreakersOpen = orderedModels.every((m) => {
- const p = parseModel(m).provider || parseModel(m).providerAlias || "unknown";
- return !getCircuitBreaker(`combo:${p}`).canExecute();
+ return !getCircuitBreaker(`combo:${m}`).canExecute();
});
if (allBreakersOpen) {
diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx
index 1bebb37da0..a3c6d6ea1b 100644
--- a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx
+++ b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx
@@ -1338,6 +1338,8 @@ function CustomModelsSection({ providerId, providerAlias, copied, onCopy }) {
const [customModels, setCustomModels] = useState([]);
const [newModelId, setNewModelId] = useState("");
const [newModelName, setNewModelName] = useState("");
+ const [newApiFormat, setNewApiFormat] = useState("chat-completions");
+ const [newEndpoints, setNewEndpoints] = useState(["chat"]);
const [adding, setAdding] = useState(false);
const [loading, setLoading] = useState(true);
@@ -1370,11 +1372,15 @@ function CustomModelsSection({ providerId, providerAlias, copied, onCopy }) {
provider: providerId,
modelId: newModelId.trim(),
modelName: newModelName.trim() || undefined,
+ apiFormat: newApiFormat,
+ supportedEndpoints: newEndpoints,
}),
});
if (res.ok) {
setNewModelId("");
setNewModelName("");
+ setNewApiFormat("chat-completions");
+ setNewEndpoints(["chat"]);
await fetchCustomModels();
}
} catch (e) {
@@ -1407,38 +1413,89 @@ function CustomModelsSection({ providerId, providerAlias, copied, onCopy }) {
{t("customModelsHint")}
{/* Add form */}
-
-
-
-
setNewModelId(e.target.value)}
- onKeyDown={(e) => e.key === "Enter" && handleAdd()}
- placeholder={t("customModelPlaceholder")}
- className="w-full px-3 py-2 text-sm border border-border rounded-lg bg-background focus:outline-none focus:border-primary"
- />
+
+
-
-
-
setNewModelName(e.target.value)}
- onKeyDown={(e) => e.key === "Enter" && handleAdd()}
- placeholder={t("optional")}
- className="w-full px-3 py-2 text-sm border border-border rounded-lg bg-background focus:outline-none focus:border-primary"
- />
+
+ {/* API Format + Supported Endpoints */}
+
+
+
+
+
+
+
Supported Endpoints
+
+ {["chat", "embeddings", "images", "audio"].map((ep) => (
+
+ ))}
+
+
-
{/* List */}
@@ -1457,7 +1514,7 @@ function CustomModelsSection({ providerId, providerAlias, copied, onCopy }) {
tune
{model.name || model.id}
-
+
{fullModel}
@@ -1470,6 +1527,26 @@ function CustomModelsSection({ providerId, providerAlias, copied, onCopy }) {
{copied === copyKey ? "check" : "content_copy"}
+ {model.apiFormat === "responses" && (
+
+ Responses
+
+ )}
+ {model.supportedEndpoints?.includes("embeddings") && (
+
+ 📐 Embed
+
+ )}
+ {model.supportedEndpoints?.includes("images") && (
+
+ 🖼️ Images
+
+ )}
+ {model.supportedEndpoints?.includes("audio") && (
+
+ 🔊 Audio
+
+ )}