Enhance OpenAI service with proxy support and error handling

- Introduced a method to retrieve an optional proxy base URL for production use, improving security by allowing API calls to be routed through a backend.
- Updated error handling to provide clear messages when the API key is not configured or when the proxy is not set in production.
- Refactored the web scraping processor to handle undefined configuration values more gracefully, ensuring default behaviors are maintained.
This commit is contained in:
Nikhil-Doye
2025-10-20 14:18:06 -04:00
parent 8249469c31
commit fe93de79ae
2 changed files with 74 additions and 29 deletions
+72 -28
View File
@@ -9,11 +9,19 @@ const getApiKey = (): string => {
);
};
// Initialize OpenAI client for DeepSeek
// Optional proxy base URL (recommended for production)
const getProxyBaseUrl = (): string | null => {
const fromStorage = localStorage.getItem("api_base_url");
const fromEnv = process.env.REACT_APP_API_BASE;
return fromStorage || fromEnv || null;
};
// Initialize OpenAI client for DeepSeek (fallback-only; avoid in production)
const openai = new OpenAI({
apiKey: getApiKey(),
baseURL: "https://api.deepseek.com/v1", // DeepSeek API endpoint
dangerouslyAllowBrowser: true, // Only for development - in production, use a backend
baseURL: "https://api.deepseek.com/v1",
// Keep browser use only as a last-resort fallback during local dev
dangerouslyAllowBrowser: true,
});
export interface OpenAIConfig {
@@ -36,29 +44,47 @@ export const callOpenAI = async (
config: OpenAIConfig
): Promise<OpenAIResponse> => {
try {
// Check if API key is configured
const apiKey = getApiKey();
if (
!apiKey ||
apiKey === "your_openai_api_key_here" ||
apiKey === "your_deepseek_api_key_here"
) {
// Prefer secure proxy in production
const proxyBase = getProxyBaseUrl();
if (proxyBase) {
const resp = await fetch(`${proxyBase.replace(/\/$/, "")}/llm/chat`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ prompt, config }),
});
if (!resp.ok) {
const text = await resp.text();
throw new Error(`Proxy error: ${resp.status} ${text}`);
}
const data = await resp.json();
return {
content: data.content || "",
usage: data.usage,
};
}
// Fallback (dev only): direct browser call (keys are exposed!)
if (process.env.NODE_ENV === "production") {
throw new Error(
"DeepSeek API key not configured. Please configure your API key in the settings."
"LLM proxy not configured. In production, configure REACT_APP_API_BASE or localStorage 'api_base_url' to a secure backend."
);
}
// Update the OpenAI client with the current API key
openai.apiKey = apiKey;
const apiKey = getApiKey();
if (!apiKey) {
throw new Error(
"DeepSeek API key not configured. Set it in settings for local development, or configure a proxy for production."
);
}
openai.apiKey = apiKey;
const response = await openai.chat.completions.create({
model: config.model,
messages: [
{
role: "user",
content: prompt,
},
],
messages: [{ role: "user", content: prompt }],
temperature: config.temperature,
max_tokens: config.maxTokens || 1000,
});
@@ -90,20 +116,38 @@ export const generateEmbedding = async (
text: string,
model: string = "text-embedding-ada-002"
): Promise<number[]> => {
const apiKey = getApiKey();
if (!apiKey || apiKey === "your_openai_api_key_here") {
// Prefer secure proxy in production
const proxyBase = getProxyBaseUrl();
if (proxyBase) {
const resp = await fetch(`${proxyBase.replace(/\/$/, "")}/llm/embeddings`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text, model }),
});
if (!resp.ok) {
const textBody = await resp.text();
throw new Error(`Proxy error: ${resp.status} ${textBody}`);
}
const data = await resp.json();
return data.embedding as number[];
}
// Fallback (dev only)
if (process.env.NODE_ENV === "production") {
throw new Error(
"OpenAI API key not configured. Please configure your API key in the settings."
"Embedding proxy not configured. In production, configure REACT_APP_API_BASE or localStorage 'api_base_url' to a secure backend."
);
}
const apiKey = getApiKey();
if (!apiKey) {
throw new Error(
"OpenAI API key not configured. Set it in settings for local development, or configure a proxy for production."
);
}
openai.apiKey = apiKey;
const response = await openai.embeddings.create({
model,
input: text,
});
const response = await openai.embeddings.create({ model, input: text });
return response.data[0].embedding;
};
@@ -11,7 +11,8 @@ export default async function webScrapingProcessor(
const result = await scrapeWithFirecrawl({
url: config.url,
formats: config.formats || ["markdown"],
onlyMainContent: config.onlyMainContent || true,
onlyMainContent:
config.onlyMainContent !== undefined ? config.onlyMainContent : true,
maxLength: config.maxLength || 10000,
});