Enhance image and embedding provider support

- Added new image models for GPT 5.2, 5.3, and 5.4, including capabilities for text-to-image and editing.
- Updated embedding handling to include optional dimensions in requests.
- Introduced support for custom embedding providers, allowing dynamic fetching and validation of custom nodes.
- Improved image generation handling with Codex integration, including progress tracking and error handling.
- Enhanced UI components to support adding custom embeddings and displaying their status.
This commit is contained in:
decolua
2026-04-25 16:22:30 +07:00
parent cca615eaff
commit 0b8bed5793
19 changed files with 1039 additions and 130 deletions
+19 -2
View File
@@ -7,7 +7,7 @@ import {
getProxyPoolById,
} from "@/models";
import { APIKEY_PROVIDERS } from "@/shared/constants/config";
import { FREE_TIER_PROVIDERS, WEB_COOKIE_PROVIDERS, isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers";
import { FREE_TIER_PROVIDERS, WEB_COOKIE_PROVIDERS, isOpenAICompatibleProvider, isAnthropicCompatibleProvider, isCustomEmbeddingProvider } from "@/shared/constants/providers";
export const dynamic = "force-dynamic";
@@ -104,7 +104,8 @@ export async function POST(request) {
FREE_TIER_PROVIDERS[provider] ||
isWebCookieProvider ||
isOpenAICompatibleProvider(provider) ||
isAnthropicCompatibleProvider(provider);
isAnthropicCompatibleProvider(provider) ||
isCustomEmbeddingProvider(provider);
if (!provider || !isValidProvider) {
return NextResponse.json({ error: "Invalid provider" }, { status: 400 });
@@ -146,6 +147,22 @@ export async function POST(request) {
return NextResponse.json({ error: "Only one connection is allowed for this Anthropic Compatible node" }, { status: 400 });
}
providerSpecificData = {
prefix: node.prefix,
baseUrl: node.baseUrl,
nodeName: node.name,
};
} else if (isCustomEmbeddingProvider(provider)) {
const node = await getProviderNodeById(provider);
if (!node) {
return NextResponse.json({ error: "Custom Embedding node not found" }, { status: 404 });
}
const existingConnections = await getProviderConnections({ provider });
if (existingConnections.length > 0) {
return NextResponse.json({ error: "Only one connection is allowed for this Custom Embedding node" }, { status: 400 });
}
providerSpecificData = {
prefix: node.prefix,
baseUrl: node.baseUrl,
+32 -1
View File
@@ -1,6 +1,6 @@
import { NextResponse } from "next/server";
import { getProviderNodeById } from "@/models";
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers";
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider, isCustomEmbeddingProvider } from "@/shared/constants/providers";
import { getDefaultModel } from "open-sse/config/providerModels.js";
import { resolveOllamaLocalHost } from "open-sse/config/providers.js";
@@ -35,6 +35,37 @@ export async function POST(request) {
});
}
// Custom Embedding nodes: probe /models (most embedding APIs are OpenAI-compatible)
if (isCustomEmbeddingProvider(provider)) {
const node = await getProviderNodeById(provider);
if (!node) {
return NextResponse.json({ error: "Custom Embedding node not found" }, { status: 404 });
}
const baseUrl = node.baseUrl?.replace(/\/$/, "");
const modelsRes = await fetch(`${baseUrl}/models`, {
headers: { "Authorization": `Bearer ${apiKey}` },
});
if (modelsRes.ok) {
return NextResponse.json({ valid: true });
}
// Auth errors are definitive
if (modelsRes.status === 401 || modelsRes.status === 403) {
return NextResponse.json({ valid: false, error: "Invalid API key" });
}
// Fallback: probe /embeddings with a common test model — many providers lack /models
const embedRes = await fetch(`${baseUrl}/embeddings`, {
method: "POST",
headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
body: JSON.stringify({ model: "test", input: "ping" }),
});
// 401/403 = bad key; anything else (including 400 "model not found") means key works
isValid = embedRes.status !== 401 && embedRes.status !== 403;
return NextResponse.json({
valid: isValid,
error: isValid ? null : "Invalid API key",
});
}
if (isAnthropicCompatibleProvider(provider)) {
const node = await getProviderNodeById(provider);
if (!node) {