diff --git a/app/api/webdav/[...path]/route.ts b/app/api/webdav/[...path]/route.ts index 3dd9ca3cd..5675f425d 100644 --- a/app/api/webdav/[...path]/route.ts +++ b/app/api/webdav/[...path]/route.ts @@ -1,12 +1,12 @@ import { NextRequest, NextResponse } from "next/server"; -import { STORAGE_KEY, internalWhiteWebDavEndpoints } from "../../../constant"; +import { STORAGE_KEY, internalAllowedWebDavEndpoints } from "../../../constant"; import { getServerSideConfig } from "@/app/config/server"; const config = getServerSideConfig(); -const mergedWhiteWebDavEndpoints = [ - ...internalWhiteWebDavEndpoints, - ...config.whiteWebDevEndpoints, +const mergedAllowedWebDavEndpoints = [ + ...internalAllowedWebDavEndpoints, + ...config.allowedWebDevEndpoints, ].filter((domain) => Boolean(domain.trim())); async function handle( @@ -24,7 +24,9 @@ async function handle( // Validate the endpoint to prevent potential SSRF attacks if ( - !mergedWhiteWebDavEndpoints.some((white) => endpoint?.startsWith(white)) + !mergedAllowedWebDavEndpoints.some((allowedEndpoint) => + endpoint?.startsWith(allowedEndpoint), + ) ) { return NextResponse.json( { diff --git a/app/client/platforms/anthropic.ts b/app/client/platforms/anthropic.ts index efadabebe..c2b62ca04 100644 --- a/app/client/platforms/anthropic.ts +++ b/app/client/platforms/anthropic.ts @@ -181,6 +181,13 @@ export class ClaudeApi implements LLMApi { }; }); + if (prompt[0]?.role === "assistant") { + prompt.unshift({ + role: "user", + content: ";", + }); + } + const requestBody: AnthropicChatRequest = { messages: prompt, stream: shouldStream, diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 70f2e515e..799992f3a 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -1256,6 +1256,7 @@ function _Chat() { if (payload.url) { accessStore.update((access) => (access.openaiUrl = payload.url!)); } + accessStore.update((access) => (access.useCustomConfig = true)); }); } } catch { diff --git a/app/config/server.ts b/app/config/server.ts index 1beeef777..829c000d9 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -51,6 +51,22 @@ const ACCESS_CODES = (function getAccessCodes(): Set { } })(); +function getApiKey(keys?: string) { + const apiKeyEnvVar = keys ?? ""; + const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim()); + const randomIndex = Math.floor(Math.random() * apiKeys.length); + const apiKey = apiKeys[randomIndex]; + if (apiKey) { + console.log( + `[Server Config] using ${randomIndex + 1} of ${ + apiKeys.length + } api key - ${apiKey}`, + ); + } + + return apiKey; +} + export const getServerSideConfig = () => { if (typeof process === "undefined") { throw Error( @@ -74,34 +90,34 @@ export const getServerSideConfig = () => { const isGoogle = !!process.env.GOOGLE_API_KEY; const isAnthropic = !!process.env.ANTHROPIC_API_KEY; - const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? ""; - const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim()); - const randomIndex = Math.floor(Math.random() * apiKeys.length); - const apiKey = apiKeys[randomIndex]; - console.log( - `[Server Config] using ${randomIndex + 1} of ${apiKeys.length} api key`, - ); + // const apiKeyEnvVar = process.env.OPENAI_API_KEY ?? ""; + // const apiKeys = apiKeyEnvVar.split(",").map((v) => v.trim()); + // const randomIndex = Math.floor(Math.random() * apiKeys.length); + // const apiKey = apiKeys[randomIndex]; + // console.log( + // `[Server Config] using ${randomIndex + 1} of ${apiKeys.length} api key`, + // ); - const whiteWebDevEndpoints = (process.env.WHITE_WEBDEV_ENDPOINTS ?? "").split( - ",", - ); + const allowedWebDevEndpoints = ( + process.env.WHITE_WEBDEV_ENDPOINTS ?? "" + ).split(","); return { baseUrl: process.env.BASE_URL, - apiKey, + apiKey: getApiKey(process.env.OPENAI_API_KEY), openaiOrgId: process.env.OPENAI_ORG_ID, isAzure, azureUrl: process.env.AZURE_URL, - azureApiKey: process.env.AZURE_API_KEY, + azureApiKey: getApiKey(process.env.AZURE_API_KEY), azureApiVersion: process.env.AZURE_API_VERSION, isGoogle, - googleApiKey: process.env.GOOGLE_API_KEY, + googleApiKey: getApiKey(process.env.GOOGLE_API_KEY), googleUrl: process.env.GEMINI_BASE_URL ?? process.env.GOOGLE_URL, isAnthropic, - anthropicApiKey: process.env.ANTHROPIC_API_KEY, + anthropicApiKey: getApiKey(process.env.ANTHROPIC_API_KEY), anthropicApiVersion: process.env.ANTHROPIC_API_VERSION, anthropicUrl: process.env.ANTHROPIC_URL, @@ -120,8 +136,6 @@ export const getServerSideConfig = () => { disableFastLink: !!process.env.DISABLE_FAST_LINK, customModels, defaultModel, - whiteWebDevEndpoints, - isStoreFileToLocal: !!process.env.NEXT_PUBLIC_ENABLE_NODEJS_PLUGIN && !process.env.R2_ACCOUNT_ID && @@ -133,5 +147,6 @@ export const getServerSideConfig = () => { ragChunkSize: process.env.RAG_CHUNK_SIZE ?? "2000", ragChunkOverlap: process.env.RAG_CHUNK_OVERLAP ?? "200", ragReturnCount: process.env.RAG_RETURN_COUNT ?? "4", + allowedWebDevEndpoints, }; }; diff --git a/app/constant.ts b/app/constant.ts index 923147ef7..d08c4cd1f 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -218,7 +218,7 @@ export const CHAT_PAGE_SIZE = 15; export const MAX_RENDER_MSG_COUNT = 45; // some famous webdav endpoints -export const internalWhiteWebDavEndpoints = [ +export const internalAllowedWebDavEndpoints = [ "https://dav.jianguoyun.com/dav/", "https://dav.dropdav.com/", "https://dav.box.com/dav", diff --git a/app/store/chat.ts b/app/store/chat.ts index 77c16df3a..1b4727f55 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -28,6 +28,8 @@ export interface ChatToolMessage { import { createPersistStore } from "../utils/store"; import { FileInfo } from "../client/platforms/utils"; import { identifyDefaultClaudeModel } from "../utils/checkers"; +import { collectModelsWithDefaultModel } from "../utils/model"; +import { useAccessStore } from "./access"; export type ChatMessage = RequestMessage & { date: string; @@ -102,9 +104,19 @@ function getSummarizeModel(currentModel: string) { if (model.provider.providerType === "google") return GEMINI_SUMMARIZE_MODEL; // if it is using gpt-* models, force to use 3.5 to summarize if (currentModel.startsWith("gpt")) { - return SUMMARIZE_MODEL; + const configStore = useAppConfig.getState(); + const accessStore = useAccessStore.getState(); + const allModel = collectModelsWithDefaultModel( + configStore.models, + [configStore.customModels, accessStore.customModels].join(","), + accessStore.defaultModel, + ); + const summarizeModel = allModel.find( + (m) => m.name === SUMMARIZE_MODEL && m.available, + ); + return summarizeModel?.name ?? currentModel; } - if (currentModel.startsWith("gemini-pro")) { + if (currentModel.startsWith("gemini")) { return GEMINI_SUMMARIZE_MODEL; } return currentModel; diff --git a/app/utils/model.ts b/app/utils/model.ts index 6477640aa..056fff2e9 100644 --- a/app/utils/model.ts +++ b/app/utils/model.ts @@ -64,13 +64,10 @@ export function collectModelTableWithDefaultModel( ) { let modelTable = collectModelTable(models, customModels); if (defaultModel && defaultModel !== "") { - delete modelTable[defaultModel]; modelTable[defaultModel] = { + ...modelTable[defaultModel], name: defaultModel, - displayName: defaultModel, available: true, - provider: - modelTable[defaultModel]?.provider ?? customProvider(defaultModel), isDefault: true, }; }