= 12"
+ }
+ },
"node_modules/data-urls": {
"version": "3.0.2",
"dev": true,
@@ -4491,6 +4500,38 @@
"bser": "2.1.1"
}
},
+ "node_modules/fetch-blob": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "^1.0.0",
+ "web-streams-polyfill": "^3.0.3"
+ },
+ "engines": {
+ "node": "^12.20 || >= 14.13"
+ }
+ },
+ "node_modules/fetch-blob/node_modules/web-streams-polyfill": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/file-entry-cache": {
"version": "8.0.0",
"dev": true,
@@ -4628,6 +4669,18 @@
"node": ">= 12.20"
}
},
+ "node_modules/formdata-polyfill": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "license": "MIT",
+ "dependencies": {
+ "fetch-blob": "^3.1.2"
+ },
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
"node_modules/forwarded": {
"version": "0.2.0",
"license": "MIT",
@@ -6683,7 +6736,9 @@
}
},
"node_modules/openai": {
- "version": "4.98.0",
+ "version": "4.104.0",
+ "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz",
+ "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==",
"license": "Apache-2.0",
"dependencies": {
"@types/node": "^18.11.18",
@@ -9150,6 +9205,9 @@
"cors": "^2.8.5",
"dotenv": "^16.5.0",
"express": "^5.1.0",
+ "http-proxy-agent": "^7.0.2",
+ "https-proxy-agent": "^7.0.6",
+ "node-fetch": "^3.3.2",
"openai": "^4.95.1",
"ws": "^8.18.0",
"zod": "^3.25.76"
@@ -9164,6 +9222,59 @@
"tsx": "^4.19.0",
"typescript": "^5.6.2"
}
+ },
+ "server/node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "server/node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "server/node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "server/node_modules/node-fetch": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+ "license": "MIT",
+ "dependencies": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/node-fetch"
+ }
}
}
}
diff --git a/server/package.json b/server/package.json
index 0cf34f6ed..b2edaa0bf 100644
--- a/server/package.json
+++ b/server/package.json
@@ -31,6 +31,9 @@
"cors": "^2.8.5",
"dotenv": "^16.5.0",
"express": "^5.1.0",
+ "http-proxy-agent": "^7.0.2",
+ "https-proxy-agent": "^7.0.6",
+ "node-fetch": "^3.3.2",
"openai": "^4.95.1",
"ws": "^8.18.0",
"zod": "^3.25.76"
diff --git a/server/src/index.ts b/server/src/index.ts
index 5a56c2d09..f4bde8881 100644
--- a/server/src/index.ts
+++ b/server/src/index.ts
@@ -4,9 +4,34 @@ import cors from "cors";
import { parseArgs } from "node:util";
import { parse as shellParseArgs } from "shell-quote";
import * as dotenv from "dotenv";
-
-// Load environment variables from .env file
-dotenv.config();
+import { fileURLToPath } from "node:url";
+import { dirname, join } from "node:path";
+import nodeFetch from "node-fetch";
+import { HttpProxyAgent } from "http-proxy-agent";
+
+// Load environment variables from .env file in the server directory
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+dotenv.config({ path: join(__dirname, "..", ".env") });
+
+// HTTP proxy agent for routing through certproxy (port 7891)
+const httpProxyAgent = new HttpProxyAgent("http://127.0.0.1:7891");
+
+// Custom fetch implementation using node-fetch to bypass Node.js undici's port 10080 blocking
+// This is required for Stripe's service mesh which uses envoy on port 10080
+// For local development, it uses certproxy to access litellm.corp.stripe.com
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const fetchPolyfill = async (url: any, init?: any): Promise
=> {
+ const urlStr = url.toString();
+ // Use HTTP proxy for Stripe corp domains via certproxy
+ if (urlStr.includes("litellm.corp.stripe.com")) {
+ console.log(`[fetchPolyfill] Using HTTP proxy for: ${urlStr}`);
+ return nodeFetch(urlStr, { ...init, agent: httpProxyAgent });
+ }
+ // For service mesh URLs (envoy) or other URLs, use node-fetch directly
+ console.log(`[fetchPolyfill] Direct fetch for: ${urlStr}`);
+ return nodeFetch(urlStr, init);
+};
import {
SSEClientTransport,
@@ -533,12 +558,19 @@ app.post(
},
);
+// For local development, use http://litellm.corp.stripe.com via certproxy (HTTP proxy on port 7891)
+// For service mesh, set LITELLM_BASE_URL=http://litellm-srv.service.envoy:10080/v1
+const litellmBaseUrl =
+ process.env.LITELLM_BASE_URL ??
+ process.env.OPENAI_BASE_URL ??
+ "http://litellm.corp.stripe.com/v1";
+const litellmApiKey =
+ process.env.LITELLM_API_KEY ?? process.env.OPENAI_API_KEY ?? "use_case=librechat";
+
const openai = new OpenAI({
- baseURL: `${process.env.HTTP_PROXY}/v1/`,
- apiKey: process.env.OPEN_AI_KEY,
- defaultHeaders: {
- Host: process.env.OPEN_AI_HOST,
- },
+ baseURL: litellmBaseUrl,
+ apiKey: litellmApiKey,
+ fetch: fetchPolyfill, // polyfill with node-fetch because Node's built-in undici fetch blocks port 10080
});
app.post("/chat", express.json(), async (req, res) => {