mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-09-08 12:20:20 +08:00
fix: prevent user API keys from using server's baseURL
When users provide their own API key but not a custom baseURL, the code was incorrectly falling back to the server's environment variable for baseURL. This caused user API keys to be sent to the server's custom proxy endpoint instead of the provider's official endpoint, resulting in 'API key format incorrect' errors. This fix ensures that when a user provides their own API key: - Only the user's baseUrl is used (if provided) - Otherwise, the provider's official/default endpoint is used - Server's baseURL env vars are never mixed with user credentials Affected providers: openai, anthropic, google, azure, openrouter, deepseek, siliconflow, sglang, gateway, doubao, modelscope Also fixes Azure's resourceName to not leak server config to user keys. Fixes #577
This commit is contained in:
+50
-19
@@ -595,7 +595,10 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
|
||||
|
||||
case "openai": {
|
||||
const apiKey = overrides?.apiKey || process.env.OPENAI_API_KEY
|
||||
const baseURL = overrides?.baseUrl || process.env.OPENAI_BASE_URL
|
||||
// Only fall back to server's baseURL if user is NOT providing their own API key
|
||||
const baseURL = overrides?.apiKey
|
||||
? overrides?.baseUrl
|
||||
: overrides?.baseUrl || process.env.OPENAI_BASE_URL
|
||||
if (baseURL) {
|
||||
// Custom base URL = third-party proxy, use Chat Completions API
|
||||
// for compatibility (most proxies don't support /responses endpoint)
|
||||
@@ -614,8 +617,10 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
|
||||
|
||||
case "anthropic": {
|
||||
const apiKey = overrides?.apiKey || process.env.ANTHROPIC_API_KEY
|
||||
const baseURL =
|
||||
overrides?.baseUrl ||
|
||||
// Only fall back to server's baseURL if user is NOT providing their own API key
|
||||
const baseURL = overrides?.apiKey
|
||||
? overrides?.baseUrl || "https://api.anthropic.com/v1"
|
||||
: overrides?.baseUrl ||
|
||||
process.env.ANTHROPIC_BASE_URL ||
|
||||
"https://api.anthropic.com/v1"
|
||||
const customProvider = createAnthropic({
|
||||
@@ -632,7 +637,10 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
|
||||
case "google": {
|
||||
const apiKey =
|
||||
overrides?.apiKey || process.env.GOOGLE_GENERATIVE_AI_API_KEY
|
||||
const baseURL = overrides?.baseUrl || process.env.GOOGLE_BASE_URL
|
||||
// Only fall back to server's baseURL if user is NOT providing their own API key
|
||||
const baseURL = overrides?.apiKey
|
||||
? overrides?.baseUrl
|
||||
: overrides?.baseUrl || process.env.GOOGLE_BASE_URL
|
||||
if (baseURL || overrides?.apiKey) {
|
||||
const customGoogle = createGoogleGenerativeAI({
|
||||
apiKey,
|
||||
@@ -647,8 +655,14 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
|
||||
|
||||
case "azure": {
|
||||
const apiKey = overrides?.apiKey || process.env.AZURE_API_KEY
|
||||
const baseURL = overrides?.baseUrl || process.env.AZURE_BASE_URL
|
||||
const resourceName = process.env.AZURE_RESOURCE_NAME
|
||||
// Only fall back to server's baseURL if user is NOT providing their own API key
|
||||
const baseURL = overrides?.apiKey
|
||||
? overrides?.baseUrl
|
||||
: overrides?.baseUrl || process.env.AZURE_BASE_URL
|
||||
// Only use server's resourceName if user is NOT providing their own API key
|
||||
const resourceName = overrides?.apiKey
|
||||
? undefined
|
||||
: process.env.AZURE_RESOURCE_NAME
|
||||
// Azure requires either baseURL or resourceName to construct the endpoint
|
||||
// resourceName constructs: https://{resourceName}.openai.azure.com/openai/v1{path}
|
||||
if (baseURL || resourceName || overrides?.apiKey) {
|
||||
@@ -678,8 +692,10 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
|
||||
|
||||
case "openrouter": {
|
||||
const apiKey = overrides?.apiKey || process.env.OPENROUTER_API_KEY
|
||||
const baseURL =
|
||||
overrides?.baseUrl || process.env.OPENROUTER_BASE_URL
|
||||
// Only fall back to server's baseURL if user is NOT providing their own API key
|
||||
const baseURL = overrides?.apiKey
|
||||
? overrides?.baseUrl
|
||||
: overrides?.baseUrl || process.env.OPENROUTER_BASE_URL
|
||||
const openrouter = createOpenRouter({
|
||||
apiKey,
|
||||
...(baseURL && { baseURL }),
|
||||
@@ -690,7 +706,10 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
|
||||
|
||||
case "deepseek": {
|
||||
const apiKey = overrides?.apiKey || process.env.DEEPSEEK_API_KEY
|
||||
const baseURL = overrides?.baseUrl || process.env.DEEPSEEK_BASE_URL
|
||||
// Only fall back to server's baseURL if user is NOT providing their own API key
|
||||
const baseURL = overrides?.apiKey
|
||||
? overrides?.baseUrl
|
||||
: overrides?.baseUrl || process.env.DEEPSEEK_BASE_URL
|
||||
if (baseURL || overrides?.apiKey) {
|
||||
const customDeepSeek = createDeepSeek({
|
||||
apiKey,
|
||||
@@ -705,8 +724,10 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
|
||||
|
||||
case "siliconflow": {
|
||||
const apiKey = overrides?.apiKey || process.env.SILICONFLOW_API_KEY
|
||||
const baseURL =
|
||||
overrides?.baseUrl ||
|
||||
// Only fall back to server's baseURL if user is NOT providing their own API key
|
||||
const baseURL = overrides?.apiKey
|
||||
? overrides?.baseUrl || "https://api.siliconflow.cn/v1"
|
||||
: overrides?.baseUrl ||
|
||||
process.env.SILICONFLOW_BASE_URL ||
|
||||
"https://api.siliconflow.cn/v1"
|
||||
const siliconflowProvider = createOpenAI({
|
||||
@@ -719,11 +740,14 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
|
||||
|
||||
case "sglang": {
|
||||
const apiKey = overrides?.apiKey || process.env.SGLANG_API_KEY
|
||||
const baseURL = overrides?.baseUrl || process.env.SGLANG_BASE_URL
|
||||
// Only fall back to server's baseURL if user is NOT providing their own API key
|
||||
const baseURL = overrides?.apiKey
|
||||
? overrides?.baseUrl
|
||||
: overrides?.baseUrl || process.env.SGLANG_BASE_URL
|
||||
|
||||
const sglangProvider = createOpenAI({
|
||||
apiKey,
|
||||
baseURL,
|
||||
...(baseURL && { baseURL }),
|
||||
// Add a custom fetch wrapper to intercept and fix the stream from sglang
|
||||
fetch: async (url, options) => {
|
||||
const response = await fetch(url, options)
|
||||
@@ -828,8 +852,10 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
|
||||
// Model format: "provider/model" e.g., "openai/gpt-4o", "anthropic/claude-sonnet-4-5"
|
||||
// See: https://vercel.com/ai-gateway
|
||||
const apiKey = overrides?.apiKey || process.env.AI_GATEWAY_API_KEY
|
||||
const baseURL =
|
||||
overrides?.baseUrl || process.env.AI_GATEWAY_BASE_URL
|
||||
// Only fall back to server's baseURL if user is NOT providing their own API key
|
||||
const baseURL = overrides?.apiKey
|
||||
? overrides?.baseUrl
|
||||
: overrides?.baseUrl || process.env.AI_GATEWAY_BASE_URL
|
||||
// Only use custom configuration if explicitly set (local dev or custom Gateway)
|
||||
// Otherwise undefined → AI SDK uses Vercel default (https://ai-gateway.vercel.sh/v1/ai) + OIDC
|
||||
if (baseURL || overrides?.apiKey) {
|
||||
@@ -861,8 +887,11 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
|
||||
|
||||
case "doubao": {
|
||||
const apiKey = overrides?.apiKey || process.env.DOUBAO_API_KEY
|
||||
const baseURL =
|
||||
overrides?.baseUrl ||
|
||||
// Only fall back to server's baseURL if user is NOT providing their own API key
|
||||
const baseURL = overrides?.apiKey
|
||||
? overrides?.baseUrl ||
|
||||
"https://ark.cn-beijing.volces.com/api/v3"
|
||||
: overrides?.baseUrl ||
|
||||
process.env.DOUBAO_BASE_URL ||
|
||||
"https://ark.cn-beijing.volces.com/api/v3"
|
||||
const lowerModelId = modelId.toLowerCase()
|
||||
@@ -888,8 +917,10 @@ export function getAIModel(overrides?: ClientOverrides): ModelConfig {
|
||||
|
||||
case "modelscope": {
|
||||
const apiKey = overrides?.apiKey || process.env.MODELSCOPE_API_KEY
|
||||
const baseURL =
|
||||
overrides?.baseUrl ||
|
||||
// Only fall back to server's baseURL if user is NOT providing their own API key
|
||||
const baseURL = overrides?.apiKey
|
||||
? overrides?.baseUrl || "https://api-inference.modelscope.cn/v1"
|
||||
: overrides?.baseUrl ||
|
||||
process.env.MODELSCOPE_BASE_URL ||
|
||||
"https://api-inference.modelscope.cn/v1"
|
||||
const modelscopeProvider = createOpenAI({
|
||||
|
||||
Reference in New Issue
Block a user