Spaces:
Runtime error
Runtime error
| import { NextRequest, NextResponse } from "next/server"; | |
| import { isAuthenticated } from "@/shared/utils/apiAuth"; | |
| import { AI_MODELS } from "@/shared/constants/models"; | |
| import { getProviderConnections } from "@/lib/db/providers"; | |
| import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts"; | |
| type EmbeddingModelOption = { | |
| value: string; | |
| label: string; | |
| }; | |
| function isLikelyEmbeddingModel(provider: string, model: string, name: string): boolean { | |
| const haystack = `${provider}/${model} ${name}`.toLowerCase(); | |
| if (haystack.includes("embedding")) return true; | |
| if (haystack.includes("embed")) return true; | |
| if (haystack.includes("text-embedding")) return true; | |
| return false; | |
| } | |
| export async function GET(request: NextRequest) { | |
| if (!(await isAuthenticated(request))) { | |
| return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); | |
| } | |
| try { | |
| const options: EmbeddingModelOption[] = AI_MODELS.filter((m: any) => | |
| isLikelyEmbeddingModel(String(m.provider || ""), String(m.model || ""), String(m.name || "")) | |
| ) | |
| .map((m: any) => ({ | |
| value: `${m.provider}/${m.model}`, | |
| label: `${m.provider}/${m.model} - ${m.name}`, | |
| })) | |
| .sort((a, b) => a.value.localeCompare(b.value)); // teknik sıralama: ASCII kasıtlı | |
| // Add OpenRouter account models that explicitly support embeddings. | |
| try { | |
| const connections = (await getProviderConnections({ | |
| provider: "openrouter", | |
| isActive: true, | |
| })) as Array<Record<string, unknown>>; | |
| const apiKey = connections.find( | |
| (c) => typeof c.apiKey === "string" && (c.apiKey as string).trim().length > 0 | |
| )?.apiKey as string | undefined; | |
| if (apiKey) { | |
| const controller = new AbortController(); | |
| const timeout = setTimeout(() => controller.abort(), 7000); | |
| let res: Response; | |
| try { | |
| res = await fetch("https://openrouter.ai/api/v1/models?output_modalities=embeddings", { | |
| method: "GET", | |
| headers: { | |
| Authorization: `Bearer ${apiKey}`, | |
| }, | |
| cache: "no-store", | |
| signal: controller.signal, | |
| }); | |
| } finally { | |
| clearTimeout(timeout); | |
| } | |
| if (res.ok) { | |
| const data = (await res.json().catch(() => null)) as any; | |
| const rows = Array.isArray(data?.data) ? data.data : []; | |
| for (const row of rows) { | |
| const id = typeof row?.id === "string" ? row.id.trim() : ""; | |
| if (!id) continue; | |
| const value = `openrouter/${id}`; | |
| if (options.some((o) => o.value === value)) continue; | |
| options.push({ | |
| value, | |
| label: `${value} - ${String(row?.name || id)}`, | |
| }); | |
| } | |
| } | |
| } | |
| } catch { | |
| // Best effort only: keep endpoint fast and resilient. | |
| } | |
| // Ensure the default always exists as a safe fallback. | |
| if (!options.some((o) => o.value === "openai/text-embedding-3-small")) { | |
| options.unshift({ | |
| value: "openai/text-embedding-3-small", | |
| label: "openai/text-embedding-3-small - OpenAI Text Embedding 3 Small", | |
| }); | |
| } | |
| options.sort((a, b) => a.value.localeCompare(b.value)); // teknik sıralama: ASCII kasıtlı | |
| return NextResponse.json({ models: options }); | |
| } catch (error) { | |
| const message = sanitizeErrorMessage(error instanceof Error ? error.message : String(error)); | |
| return NextResponse.json({ error: { message }, models: [] }, { status: 500 }); | |
| } | |
| } | |