From 8e4565dacb8e0b843c01abd766cddb2f67ebc8cc Mon Sep 17 00:00:00 2001 From: Martin Mose Facondini Date: Thu, 23 Apr 2026 23:48:41 +0200 Subject: [PATCH] feat: add OPENAI_BASE_URL and OPENAI_EMBEDDING_MODEL env var support Allow using OpenAI-compatible APIs (e.g. LiteLLM) for embeddings by reading OPENAI_BASE_URL (defaults to https://api.openai.com/v1) and OPENAI_EMBEDDING_MODEL (defaults to text-embedding-3-small) from environment variables instead of hardcoding them. This enables self-hosted deployments to use any OpenAI-compatible embeddings provider. --- convex/api.ts | 5 +++-- convex/embeddings.ts | 7 ++++--- convex/search.ts | 7 ++++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/convex/api.ts b/convex/api.ts index 458b9eb..cb5ff59 100644 --- a/convex/api.ts +++ b/convex/api.ts @@ -504,14 +504,15 @@ export const semanticSearch = internalAction({ const apiKey = process.env.OPENAI_API_KEY; if (!apiKey) throw new Error("OPENAI_API_KEY not set"); - const response = await fetch("https://api.openai.com/v1/embeddings", { + const baseUrl = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1"; + const response = await fetch(`${baseUrl}/embeddings`, { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ - model: "text-embedding-3-small", + model: process.env.OPENAI_EMBEDDING_MODEL || "text-embedding-3-small", input: query.slice(0, 8000), }), }); diff --git a/convex/embeddings.ts b/convex/embeddings.ts index d512f6f..d042279 100644 --- a/convex/embeddings.ts +++ b/convex/embeddings.ts @@ -14,19 +14,20 @@ function hashText(text: string): string { return hash.toString(16); } -// Generate embedding via OpenAI +// Generate embedding via OpenAI-compatible API async function embed(text: string): Promise { const apiKey = process.env.OPENAI_API_KEY; if (!apiKey) throw new Error("OPENAI_API_KEY not set"); - const response = await fetch("https://api.openai.com/v1/embeddings", { + const baseUrl = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1"; + const response = await fetch(`${baseUrl}/embeddings`, { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ - model: "text-embedding-3-small", + model: process.env.OPENAI_EMBEDDING_MODEL || "text-embedding-3-small", input: text.slice(0, 8000), }), }); diff --git a/convex/search.ts b/convex/search.ts index 7fec5bc..e806d93 100644 --- a/convex/search.ts +++ b/convex/search.ts @@ -571,19 +571,20 @@ export const loadSessionsFromEmbeddings = internalQuery({ }, }); -// Helper: generate embedding via OpenAI +// Helper: generate embedding via OpenAI-compatible API async function generateEmbedding(text: string): Promise { const apiKey = process.env.OPENAI_API_KEY; if (!apiKey) throw new Error("OPENAI_API_KEY not set"); - const response = await fetch("https://api.openai.com/v1/embeddings", { + const baseUrl = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1"; + const response = await fetch(`${baseUrl}/embeddings`, { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ - model: "text-embedding-3-small", + model: process.env.OPENAI_EMBEDDING_MODEL || "text-embedding-3-small", input: text.slice(0, 8000), }), });