diff --git a/app/api/config/route.ts b/app/api/config/route.ts index b0d9da031..855a5db01 100644 --- a/app/api/config/route.ts +++ b/app/api/config/route.ts @@ -14,6 +14,7 @@ const DANGER_CONFIG = { disableFastLink: serverConfig.disableFastLink, customModels: serverConfig.customModels, defaultModel: serverConfig.defaultModel, + visionModels: serverConfig.visionModels, }; declare global { diff --git a/app/api/deepseek.ts b/app/api/deepseek.ts index 06d97a0d6..a9879eced 100644 --- a/app/api/deepseek.ts +++ b/app/api/deepseek.ts @@ -91,7 +91,7 @@ async function request(req: NextRequest) { isModelNotavailableInServer( serverConfig.customModels, jsonBody?.model as string, - ServiceProvider.Moonshot as string, + ServiceProvider.DeepSeek as string, ) ) { return NextResponse.json( diff --git a/app/components/chat.tsx b/app/components/chat.tsx index f34f7d78e..9990a359e 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -900,6 +900,12 @@ export function ShortcutKeyModal(props: { onClose: () => void }) { title: Locale.Chat.ShortcutKey.showShortcutKey, keys: isMac ? ["⌘", "/"] : ["Ctrl", "/"], }, + { + title: Locale.Chat.ShortcutKey.clearContext, + keys: isMac + ? ["⌘", "Shift", "backspace"] + : ["Ctrl", "Shift", "backspace"], + }, ]; return (
@@ -1552,7 +1558,7 @@ function _Chat() { const [showShortcutKeyModal, setShowShortcutKeyModal] = useState(false); useEffect(() => { - const handleKeyDown = (event: any) => { + const handleKeyDown = (event: KeyboardEvent) => { // 打开新聊天 command + shift + o if ( (event.metaKey || event.ctrlKey) && @@ -1603,14 +1609,30 @@ function _Chat() { event.preventDefault(); setShowShortcutKeyModal(true); } + // 清除上下文 command + shift + backspace + else if ( + (event.metaKey || event.ctrlKey) && + event.shiftKey && + event.key.toLowerCase() === "backspace" + ) { + event.preventDefault(); + chatStore.updateTargetSession(session, (session) => { + if (session.clearContextIndex === session.messages.length) { + session.clearContextIndex = undefined; + } else { + session.clearContextIndex = session.messages.length; + session.memoryPrompt = ""; // will clear memory + } + }); + } }; - window.addEventListener("keydown", handleKeyDown); + document.addEventListener("keydown", handleKeyDown); return () => { - window.removeEventListener("keydown", handleKeyDown); + document.removeEventListener("keydown", handleKeyDown); }; - }, [messages, chatStore, navigate]); + }, [messages, chatStore, navigate, session]); const [showChatSidePanel, setShowChatSidePanel] = useState(false); diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 5a16039b7..41b0f881d 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -72,6 +72,7 @@ import { Iflytek, SAAS_CHAT_URL, ChatGLM, + DeepSeek, } from "../constant"; import { Prompt, SearchService, usePromptStore } from "../store/prompt"; import { ErrorBoundary } from "./error"; @@ -1278,6 +1279,47 @@ export function Settings() { ); + const deepseekConfigComponent = accessStore.provider === + ServiceProvider.DeepSeek && ( + <> + + + accessStore.update( + (access) => (access.deepseekUrl = e.currentTarget.value), + ) + } + > + + + { + accessStore.update( + (access) => (access.deepseekApiKey = e.currentTarget.value), + ); + }} + /> + + + ); + const XAIConfigComponent = accessStore.provider === ServiceProvider.XAI && ( <> { buildMode, isApp, template: process.env.DEFAULT_INPUT_TEMPLATE ?? DEFAULT_INPUT_TEMPLATE, - visionModels: process.env.VISION_MODELS || "", }; }; diff --git a/app/config/server.ts b/app/config/server.ts index ced406e90..367f65738 100644 --- a/app/config/server.ts +++ b/app/config/server.ts @@ -29,6 +29,7 @@ declare global { DISABLE_FAST_LINK?: string; // disallow parse settings from url or not CUSTOM_MODELS?: string; // to control custom models DEFAULT_MODEL?: string; // to control default model in every new chat window + VISION_MODELS?: string; // to control vision models // stability only STABILITY_URL?: string; @@ -134,6 +135,7 @@ export const getServerSideConfig = () => { const disableGPT4 = !!process.env.DISABLE_GPT4; let customModels = process.env.CUSTOM_MODELS ?? ""; let defaultModel = process.env.DEFAULT_MODEL ?? ""; + let visionModels = process.env.VISION_MODELS ?? ""; if (disableGPT4) { if (customModels) customModels += ","; @@ -264,6 +266,7 @@ export const getServerSideConfig = () => { disableFastLink: !!process.env.DISABLE_FAST_LINK, customModels, defaultModel, + visionModels, allowedWebDavEndpoints, }; }; diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 5773b68c6..3ec07b8aa 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -106,6 +106,7 @@ const cn = { copyLastMessage: "复制最后一个回复", copyLastCode: "复制最后一个代码块", showShortcutKey: "显示快捷方式", + clearContext: "清除上下文", }, }, Export: { @@ -488,6 +489,17 @@ const cn = { SubTitle: "样例:", }, }, + DeepSeek: { + ApiKey: { + Title: "接口密钥", + SubTitle: "使用自定义DeepSeek API Key", + Placeholder: "DeepSeek API Key", + }, + Endpoint: { + Title: "接口地址", + SubTitle: "样例:", + }, + }, XAI: { ApiKey: { Title: "接口密钥", diff --git a/app/locales/en.ts b/app/locales/en.ts index 5e800049e..514258a9c 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -107,6 +107,7 @@ const en: LocaleType = { copyLastMessage: "Copy Last Reply", copyLastCode: "Copy Last Code Block", showShortcutKey: "Show Shortcuts", + clearContext: "Clear Context", }, }, Export: { @@ -473,6 +474,17 @@ const en: LocaleType = { SubTitle: "Example: ", }, }, + DeepSeek: { + ApiKey: { + Title: "DeepSeek API Key", + SubTitle: "Use a custom DeepSeek API Key", + Placeholder: "DeepSeek API Key", + }, + Endpoint: { + Title: "Endpoint Address", + SubTitle: "Example: ", + }, + }, XAI: { ApiKey: { Title: "XAI API Key", diff --git a/app/locales/tw.ts b/app/locales/tw.ts index f10c793ab..83dd547b8 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -100,6 +100,7 @@ const tw = { copyLastMessage: "複製最後一個回覆", copyLastCode: "複製最後一個程式碼區塊", showShortcutKey: "顯示快捷方式", + clearContext: "清除上下文", }, }, Export: { diff --git a/app/store/access.ts b/app/store/access.ts index 4414f7ac6..fb744c14e 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -130,6 +130,7 @@ const DEFAULT_ACCESS_STATE = { disableFastLink: false, customModels: "", defaultModel: "", + visionModels: "", // tts config edgeTTSVoiceName: "zh-CN-YunxiNeural", @@ -142,7 +143,10 @@ export const useAccessStore = createPersistStore( this.fetch(); return get().needCode; }, - + getVisionModels() { + this.fetch(); + return get().visionModels; + }, edgeVoiceName() { this.fetch(); return get().edgeTTSVoiceName; diff --git a/app/utils.ts b/app/utils.ts index e7f332555..684af7a08 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -6,7 +6,7 @@ import { ServiceProvider } from "./constant"; // import { fetch as tauriFetch, ResponseType } from "@tauri-apps/api/http"; import { fetch as tauriStreamFetch } from "./utils/stream"; import { VISION_MODEL_REGEXES, EXCLUDE_VISION_MODEL_REGEXES } from "./constant"; -import { getClientConfig } from "./config/client"; +import { useAccessStore } from "./store"; import { ModelSize } from "./typing"; export function trimTopic(topic: string) { @@ -255,8 +255,8 @@ export function getMessageImages(message: RequestMessage): string[] { } export function isVisionModel(model: string) { - const clientConfig = getClientConfig(); - const envVisionModels = clientConfig?.visionModels + const visionModels = useAccessStore.getState().visionModels; + const envVisionModels = visionModels ?.split(",") .map((m) => m.trim()); if (envVisionModels?.includes(model)) {